2015-08-20 105 views
3

以下功能通過XML-RPC我的服務器上創建新的文件夾執行承諾遞歸的NodeJS

var createFolder = function(folder_name) { 
    var defer = Q.defer(); 
    client.methodCall('create_folder', [sessionID, folder_name], function(err, resp) { 
    if (err) { 
     if (err.responseString && err.responseString.match('already exist')) { 
     //call the same function recursively with folder_name+Math.round(Math.random()*100) 
     } else { 
     defer.reject(err); 
     } 
    } else { 
     defer.resolve(folder_name); 
    } 
    }); 
    return defer.promise; 
} 

功能成功地創建一個新的文件夾 但是,如果文件夾已經存在,我想觸發此功能再次遞歸地與新的文件夾命名並返回它的承諾,以便每當這個函數被調用,它會返回文件夾名稱並不重要是如何執行的次數

createFolder('directory').then(function(resp){ 
console.log(resp);// may return directory || directory1 .... etc 
}); 

**編輯** 所以我manged通過將延遲對象來實現這一 讓我知道是否有實現這一

var createFolder = function(folder_name,defer) { 
    defer =defer || Q.defer(); 
    client.methodCall('create_folder', [sessionID, folder_name], function(err, resp) { 
    if (err) { 
     if (err.responseString && err.responseString.match('already exist')) { 
     return createFolder(folder_name+Math.round(Math.random()*100,defer) 
     } else { 
     defer.reject(err); 
     } 
    } else { 
     defer.resolve(folder_name); 
    } 
    }); 
    return defer.promise; 
} 
+1

'return createFolder(...);'? – thefourtheye

+0

「*我通過傳遞延遲對象*來實現這一目標」 - 一個可怕的想法。它甚至會更好做'defer.resolve(createFolder(folder_name + Math.floor(Math.random()* 100)));' – Bergi

+0

謝謝我會嘗試重構代碼並按照你的方法 – ahhmarr

回答

1

這裏的更優雅的方式是解決的簡單的方法你的問題:

var createFolder = function(folder_name) { 
    var defer = Q.defer(); 
    client.methodCall('create_folder', [sessionID, folder_name], function(err, resp) { 
    if (err) { 
     if (err.responseString && err.responseString.match('already exist')) { 
     //call the same function recursively with folder_name+Math.round(Math.random()*100) 
     defer.resolve(createFolder(folder_name+Math.round(Math.random()*100))); 
     } else { 
     defer.reject(err); 
     } 
    } else { 
     defer.resolve(folder_name); 
    } 
    }); 
    return defer.promise; 
} 

然而,defer被認爲是不好的做法。這是一個very nice article about promises

你應該贊成這樣的:

var createFolder = function(folder_name) { 
    return Q.Promise(function(resolve, reject){ 
    client.methodCall('create_folder', [sessionID, folder_name], function(err, resp) { 
     if (err) { 
      if (err.responseString && err.responseString.match('already exist')) { 
      //call the same function recursively with folder_name+Math.round(Math.random()*100) 
      resolve(createFolder(folder_name+Math.round(Math.random()*100))); 
      } else { 
      reject(err); 
      } 
     } else { 
      resolve(folder_name); 
     } 
     }); 
    }); 
} 

編輯:由@Bergi指出,這仍然是不對的,難以調試。methodCall的回調中拋出的任何潛在錯誤都不會實際上拒絕承諾並很可能會被吞下(儘管此回調似乎很少有錯誤傾向,但可能會發生變化)。請參閱his answer以獲取更好的方法。請參閱the official Q doc here

+0

我做了在將'return'修改爲'resolve()'後,收回我的downvote,但這兩種解決方案都不應該受到青睞。 – Bergi

+0

@Bergi,你能解釋一下爲什麼嗎? –

+1

@AlexisMétaireau:首先,OP正在尋找一種優雅的方式:-)這些日子裏,延遲幾乎不被考慮。有人甚至可能會爭辯說,將它們與承諾調用一起使用類似於[延遲反模式](http://stackoverflow.com/q/23803743/1048572)。最後,還有一些微小的細微之處,比如在回調中拋出安全性,例如當'err.responseString'沒有'match'方法。 – Bergi

4

絕對不要在普通(非承諾)回調中做任何邏輯。 Promisify在最低水平:

var defer = Q.defer(); 
client.methodCall('create_folder', [sessionID, folder_name], function(err, resp) { 
    if (err) defer.reject(err); 
    else defer.resolve(folder_name); 
}); 
return defer.promise; 

或者更加簡單與Q.ninvoke

return Q.ninvoke(client, 'methodCall', 'create_folder', [sessionID, folder_name]); 

現在我們可以開始實施我們的遞歸。這是一個很簡單的回調,您可以從中返回另一個承諾then。在你的情況下:

function createFolder(folder_name) { 
    return Q.ninvoke(client, 'methodCall', 'create_folder', [sessionID, folder_name]) 
    .catch(function(err) { 
     if (err.responseString && err.responseString.match('already exist')) { 
     return createFolder(folder_name+Math.floor(Math.random()*100)); 
     } else { 
     throw err; 
     } 
    }); 
} 
+0

從我從OP的代碼中看到的,我不認爲methodCall會拋出任何東西。因此我不認爲這個代碼有效。 –

+0

@QuentinRoy:好的,如果出現錯誤,承諾就會被拒絕 - 它不一定是拋出異常。然後我們捕獲那些表明已經存在的文件夾的錯誤,然後重試。 – Bergi

+0

是的,但據我所知,錯誤是作爲提供給'methodCall'的回調的第一個參數給出的(我不能說我喜歡這個API,但它似乎是它在這裏的工作原理)。因此,捕捉不會被調用。這不是一個例外。 –