【问题标题】:execute promises recursively nodejs递归执行promise 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)));
  • 谢谢,我会尝试重构代码并遵循您的方法

标签: node.js recursion promise xml-rpc q


【解决方案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:好吧,如果出现错误,承诺将被拒绝 - 它不一定是 thrown 异常。我们捕捉到那些表明已经存在文件夹的错误,然后再试一次。
  • 是的,但据我所知,错误是作为提供给methodCall 的回调的第一个参数给出的(我不能说我喜欢这个API,但它似乎是它的工作原理这里)。结果 catch 不会被调用。这也不例外。
  • @QuentinRoy:是的,这只是 nodejs 回调(“nodeback”)约定。如果第一个参数给出错误,则promise 拒绝并且catch 回调 被调用
猜你喜欢
  • 2021-11-11
  • 2019-04-28
  • 1970-01-01
  • 1970-01-01
  • 2021-04-29
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多