【问题标题】:Node.js: Making a call to asynchronous parent function from within asynchronous child function's callbackNode.js:从异步子函数的回调中调用异步父函数
【发布时间】:2015-01-22 12:20:38
【问题描述】:

我正在尝试从另一个异步函数(“父”)中调用一个异步函数(“子”)。更复杂的是,父函数公开了一个双重 API(回调 + 承诺)。这是使用 Q 库的 defer()nodeify() 完成的,如下所示:

var parent = function(p_arg, callback) {

  var deferred = Q.defer();

  child(arg, function(err, cb_arg) {
    if(err)
      deferred.reject(err);
    else
      deferred.resolve(cb_arg);
  });

  //If a callback was supplied, register it using nodeify(). Otherwise, return a promise.
  if(callback)
    deferred.promise.nodeify(callback);
  else
    return deferred.promise;
}

请注意,子函数不返回承诺。现在根据传递给子回调 (cb_arg) 的参数值,我可能决定重新调用父级,尽管使用修改后的参数 (p_arg)。考虑到父 API 的双重性质,我该如何做到这一点?

这是我目前所能想到的:

child(arg, function(err, cb_arg) {
  if(err)
    deferred.reject(err);
  else if(cb_arg.condition) {

    /*Make a fresh call to parent, passing in a modified p_arg*/
    if(callback)
      parent(p_arg + 1, callback);
    else
      deferred.promise = parent(p_arg + 1);
      /*
        ^ Is this the correct way to do this?
        My goal is to return the promise returned by the fresh parent() call,
        so how about I just assign it to the current parent() call's deferred.promise?
      */
  } 
  else
    deferred.resolve(cb_arg);
});

更新:好的,我刚想清楚这个问题。我现在认为真正需要做的是以下几点:

child(arg, function(err, cb_arg) {
  if(err)
    deferred.reject(err);
  else if(cb_arg.condition) {

    /*Make a fresh call to parent, passing in a modified p_arg*/
    if(callback)
      parent(p_arg + 1, callback);
    else
      parent(p_arg + 1)
      .then(function(p_ret) {
          deferred.resolve(p_ret);
      }, function(p_err) {
          deferred.reject(p_err);
      });
  } 
  else
    deferred.resolve(cb_arg);
});

这种模式似乎适用于我的特定用例。但是,如果这种方法有任何明显的异步相关错误,请告诉我。

【问题讨论】:

  • 不用说== true

标签: node.js asynchronous callback promise q


【解决方案1】:

如何在记住父 API 的双重性质的情况下做到这一点?

不要。只要你有可用的承诺,就总是使用它们。

顺便说一句,您可以使用Q.ncall 而不是手动使用延迟。而且你不需要显式测试回调,如果你通过undefinednodeify 会处理它。

deferred.promise = parent(p_arg + 1);

这是正确的方法吗? 我的目标是返回新的 parent() 调用返回的承诺, 那么我将它分配给当前 parent() 调用的 deferred.promise 怎么样?

您将使用 .then() 并从回调中返回一个新的 Promise 以链接操作,从而为最后一个操作的结果获得一个新的 Promise。

var promise = child().then(function(res) {
    if (condition(res))
        return parent(); // a promise
    else
        return res; // just pass through the value
});

更新:[…] 这种模式似乎适用于我的特定用例。

这是deferred antipattern 的变体。你不想使用它。特别是,如果有回调传递,它似乎会留下一个悬空(从未解决)延迟并附加一个回调。

你应该做的只是

function parent(p_arg, callback) {
    return Q.nfcall(child, arg)
    .then(function(cb_arg) {
        if (cb_arg.condition) {
            return parent(p_arg + 1, callback);
        else
            return cb_arg;
    })
    .nodeify(callback);
}

【讨论】:

  • 我应该提到孩子没有返回承诺。对于我的特定用例,“孩子”是节点的标准 http.get()。
  • 我希望始终使用承诺。但是,我在编写我的第一个节点模块时遇到了这个问题,对此我认为提供相同的异步函数来执行回调 返回一个承诺会很酷,具体取决于开发者想要使用它。
  • 我的意思是你应该在递归调用中使用 Promise - 当你是你自己的 api 的消费者时。 child 不返回承诺并不是一个大问题,因为您的 deferred.promise child() 调用结果的承诺。
  • 非常感谢您将我指向这些链接。我很高兴在我使用 Promise 的早期就可以接触到这些反模式。我也将您的答案标记为正确,但是我还有最后一个问题。在那个 then() 处理程序中,我如何触发要由下一个对 then() 的链式调用处理的进度事件?
  • 嗯,进度事件已被弃用,尤其是因为您描述的用例难以实现。也许你应该回到child() 的延迟,并立即(但异步)触发一个“启动”事件。
猜你喜欢
  • 2019-01-20
  • 1970-01-01
  • 2018-01-04
  • 1970-01-01
  • 2020-04-20
  • 1970-01-01
  • 2020-06-04
  • 2012-07-25
  • 1970-01-01
相关资源
最近更新 更多