递归执行承诺nodejs

execute promises recursively nodejs

以下函数通过 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;
}

函数创建新文件夹成功 但是,如果文件夹已经存在,我想用新的文件夹名称递归地再次触发这个函数,然后 return 它承诺,这样无论何时调用这个函数,它都会 return 文件夹名称无关紧要执行了多少次

类似

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;
}

这是解决问题的不好简单方法:

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 的回调抛出的任何潜在错误实际上不会拒绝承诺,很可能会被吞噬(尽管这个回调看起来很少出错,但它可能会演变)。请参考 以获得更好的方法。

另外,参见 the official Q doc here

切勿在普通(非承诺)回调中执行任何逻辑。在最低级别承诺:

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 回调非常简单,您可以从中 return 另一个承诺。在你的情况下:

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;
      }
    });
}