ininfinite loop in promise catch with callback error

时间:2016-12-24 14:13:22

标签: javascript node.js promise bluebird

我很确信我误解了承诺是如何运作的,但很多人都有阅读未能解决我的问题。

我有现有的代码调用另一个刚刚更改为使用promises的模块。使用旧版本看起来像这样:

function(params, cb) {
  .....
  asyncFunc(p1, (err, res) => {
    if (!err)
       .....
       cb(null,'msg');
    else
       cb(err);
  };
};

将此更改为:

function(params, cb) {
  .....
asyncFuncwithPromise(p1).then(res = > {
    ...  // X
    return cb(null,'msg');
}).catch(err => {
    cb(err);
});

如果X行的代码抛出异常,则会进入catch() - > then()的无限循环。从catch中删除回调,一切都很好。谁能解释一下?

1 个答案:

答案 0 :(得分:0)

除非cb()正在调用asyncFuncWithPromise(),否则无法确定如何在此处创建无限循环。

我喜欢将Promise回调链视为两条平行轨道。要继续取消成功(then())跟踪,请使用return关键字。要继续关注失败(catch())跟踪,请使用throw关键字。链的轨道可以随时切换,但回调只能线性执行;也就是说,先前在链中定义的回调在稍后定义之后不能执行。链条总是向前推进,无法进入循环。

Promise.resolve()
  .then(()=>{ // 1S
    if (Math.floor(Math.random() * 2))
      return "success"; // continue down "success" (then) track
    else
      throw "fail"; // continue down "failure" (catch) track
  })
  .then(r=>{ // 2S
    return "all good: " + r;
  })
  .catch(e=>{ // 2F
    // use 'return' to recover, continue down "success" track
    return "recovered from error: " + e;
  })
  .then(r=>{ // 3S
    console.log(r); // always executed
  })
  .catch(e=> { //3F
    console.error(e); // never executed
  });

在此示例中,将始终执行包含console.log()的链中的最终成功链接(3S),因为catch()链接(2F)使用return来重新启用链成功之路。当然,如果每个链接都使用throw关键字,则可以对多个链接执行失败跟踪。

使用Promise构造函数创建新Promise时,成功和失败跟踪最初对应于解析程序中提供的resolvereject函数。所以,这之间没有区别(功能上):

function getPromise(...) {
  return new Promise((resolve, reject)=>{
    /* do something... */
    if (condition) {
      resolve(result);
    } else {
      reject(error);
    }
  });
}

和此:

function getPromise(...) {
  return Promise.resolve().then(()=>{
    /* do something... */
    if (condition)
      return result;
    else
      throw error;
  });
}

这些之间的差异巧妙地与Promise链中的链接如何遵循运行时事件框架有关。这可能因实现而异,但通常会将then()catch()回调推迟到执行帧的下一个后继者。