递归执行promise nodejs
Posted
技术标签:
【中文标题】递归执行promise nodejs【英文标题】:execute promises recursively nodejs 【发布时间】:2015-11-14 02:50:06 【问题描述】:以下函数通过 xmlrpc 在我的服务器上创建新文件夹
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;
函数成功创建新文件夹 但是,如果文件夹已经存在,我想用新的文件夹名称再次递归地触发这个函数,然后在 promise 中返回它,这样无论何时调用这个函数,它都会返回文件夹名称,不管它执行了多少次
类似
createFolder('directory').then(function(resp)
console.log(resp);// may return directory || directory1 .... etc
);
**编辑** 所以我设法通过传递延迟对象来实现这一点 让我知道是否有更优雅的方法来实现这一目标
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;
【问题讨论】:
return createFolder(...);
?
“我设法通过传递延迟对象来实现这一目标” - 一个可怕的想法。这样做会更好defer.resolve(createFolder(folder_name+Math.floor(Math.random()*100)));
谢谢,我会尝试重构代码并遵循您的方法
【参考方案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。
【讨论】:
在您将return
修复为 resolve()
后,我确实撤回了我的反对意见,但这些解决方案仍然不应该受到青睐。
@Bergi,你能解释一下原因吗?
@AlexisMétaireau:一方面,OP 正在寻找一种优雅的方式 :-) 如今几乎不考虑延期。甚至有人可能会争辩说,将它们与 promise 调用一起使用类似于 deferred antipattern。最后,还有一些小细节,例如回调中的抛出安全性,例如当err.responseString
没有match
方法时。
@Bergi 我同意你的看法。这正是我提出使用 Promise 模式的第二个版本的原因。我保留了第一个,因为我不是来告诉 OP 他应该如何编码,而是帮助他解决问题。关于匹配的事情,我们谈论的是 node.js,所以我认为这不是问题。
@Bergi,我明白你的意思。在methodCall
的回调中抛出的任何错误都将被吞噬。【参考方案2】:
永远不要在普通(非承诺)回调中执行任何逻辑。在最低级别承诺:
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;
);
【讨论】:
从我从 OP 的代码中看到的,我不认为 methodCall 抛出任何东西。因此,我认为这段代码不起作用。 @QuentinRoy:好吧,如果出现错误,承诺将被拒绝 - 它不一定是throw
n 异常。我们捕捉到那些表明已经存在文件夹的错误,然后再试一次。
是的,但据我所知,错误是作为提供给methodCall
的回调的第一个参数给出的(我不能说我喜欢这个API,但它似乎是它的工作原理这里)。结果 catch 不会被调用。这也不例外。
@QuentinRoy:是的,这只是 nodejs 回调(“nodeback”)约定。如果第一个参数给出错误,则promise 被 拒绝并且catch
回调将 被调用以上是关于递归执行promise nodejs的主要内容,如果未能解决你的问题,请参考以下文章