【问题标题】:Promises: is .done() executed always even if .catch() is?承诺:即使 .catch() 是 .done() 总是执行吗?
【发布时间】:2016-03-01 01:35:22
【问题描述】:

我的承诺问题

我是Promises 的新手,我一直在阅读Q Documentation,上面写着:

当您到达承诺链的末尾时,您应该返回最后一个承诺或结束链。

我在我的代码中以Q.Promise 方式定义了一个Promise,并使用以下console.logs 来注销执行跟踪:

function foo(){
   return Q.Promise(function(resolve, reject) {

    doSomething()
    .then(function() {
      console.log('1');
      return doSomething1();
    })
    .then(function() {
      console.log('2');
      return doSomething2();
    })
    .then(function() {
      console.log('3');
      return doSomething3();
    })
    .catch(function(err) {
      console.log('catch!!');
      reject(err);
    })
    .done(function() {
      console.log('done!!');
      resolve();
    });

  });
}

如果每个doSomethingN() 都正确执行,一切都会按预期进行,并且我会得到预期的跟踪:

1
2
3
done!!

但是万一doSomethingN() 中的任何一个失败:

foo() 工作正常,因为错误函数回调是在 reject(err) 发生时运行的回调函数:

foo().then(function() { /* */ }, function(err) { /* this runs! */ });

我得到以下跟踪(即当doSomething1() 失败时):

1
catch!!
done!!

我的问题

一开始我的想法是这样的:

好的,让我们在.done().catch() 方法中处理链接成功失败。如果一切顺利,.done() 的回调将被执行,promise 将被解决。如果在任何时候出现错误,.catch() 的回调将被执行并且承诺将被拒绝 - 因此,done() 将不会被执行。

我想我遗漏了一些关于.done() 工作原理的信息……因为通过查看我的日志记录跟踪,我意识到.done() 似乎总是在执行——是否有错误和.catch()执行与否 - 这是我没有预料到的。

因此,在那之后,我删除了.done() 的回调,现在删除了foo()

  • 如果在链执行期间有 error 则有效
  • 如果一切正常,则无法正常工作

我应该重新考虑什么以及如何/应该让它发挥作用?

【问题讨论】:

  • 你的问题不在于done,而在于catch。捕获错误意味着您已经处理了它,并且从 .catch(…) 返回的承诺 - 您将 .done() 附加到的承诺 - 将会实现!
  • @Bergi 恐怕我不明白您在第二条评论中要解释什么....catch() 有什么问题?
  • 它返回一个最终将被履行的承诺,这就是为什么你的done即使出现错误也会运行的原因。看看Chained promises not passing on rejection
  • @Bergi 您提供的有关该主题的链接非常有用!

标签: javascript node.js promise q


【解决方案1】:

catch(cb) 只是then(null, cb) 的别名,而您实际上修复了 catch 中的一个错误,所以流程自然而然地转向成功 结果done.

如果你只想装饰 catch 中的错误,你应该在之后重新抛出错误,例如正确的 passthru 可能如下所示:

catch(function (err) {
   console.log(err);
   throw err;
});

你的例子仍然没有多大意义。当你返回一个承诺时,你永远不应该使用done。如果你想用内部创建的承诺链来解决初始化的承诺,你应该把它解决为:

resolve(doSomething()
  .then(function() {
    console.log('1');
    return doSomething1();
  })
  ....
  .then(function() {
    console.log('N');
    return doSomethingN();
  }));

不需要内部错误处理,将其留给您返回的承诺消费者。

还有一点。如果在创建新的 Promise 时,您知道它将与另一个 Promise 一起解决,那么创建这样的 Promise 就没有合乎逻辑的理由,只需重用您计划解决的 Promise 即可。这种错误也被称为deferred anti-pattern

【讨论】:

  • 非常好。我唯一的问题是:用resolve() 包裹链条有什么区别?
  • 区别与resolve(promise)promise.done(resolve, reject) 相同。两者都会做完全相同的事情,但第一个更简单:)
  • 仍然像您的示例一样创建新的承诺,没有意义,您应该直接返回该链承诺。
  • 是的,当然,该链应该返回以便在调用 foo 时获得该承诺。谢谢! :-)
【解决方案2】:

你应该考虑这样做:

function foo() {
  // Calling .then() on a promise still return a promise.
  // You don't need Q.Promise here
  return doSomething()
    .then(function(doSomethingResult) {
      console.log('1');
      return doSomething1();
    })
    .then(function(doSomething1Result) {
      console.log('2');
      return doSomething2();
    })
    .then(function(doSomething2Result) {
      console.log('3');
      return doSomething3();
    });
}



foo()
  .then(function(fooResult) {
    console.log(fooResult); // fooResult should be what is returned by doSomething3()
  })
  .catch(function(err) {
    console.error(err); // Can be thrown by any 
  })
  .done(function() {
    console.log('I am always executed! error or success');
  });

如果你想返回一个 Promise,在大多数情况下使用 catch 没有多大意义(除非你想恢复潜在的错误)。在返回 Promise 的方法中使用 done 毫无意义。您宁愿在链的最末端使用这些方法。

注意doSomethingX() 可以返回一个值或一个承诺,它的工作原理是一样的。

【讨论】:

  • 很好的指出和非常简洁的解释,@SebastienLorber!我有一个关于在哪里使用deferredsnew Promises 的最后一个问题:所以,我只应该使用这些(一个或另一个)在我想要返回承诺的函数内进行“设置”。在这种情况下,在foo() 内,您不需要deferredsnew Promises,因为这已经在每个doSomethingN() 中完成(它已经返回了一个承诺),您可以直接通过.then()...我对吗?我想我知道它是如何工作的......!
  • 和@SebastienLorber,如果doSomethingN() 函数中的任何一个抛出reject... 它会传播并被foo().catch() 捕获,不是吗?
  • 另外@charliebrownie 如果您的doSomethingX() 操作不需要使用之前的结果来执行,您可以并行启动它们(使用Q.all([smth1Promise,smth2Promise,smth3Promise]).spread(res1,res2,res3)...
【解决方案3】:

您可以通过在上一个 then 回调中解析 promise 来使其工作。

function foo(){
    return doSomething()
    .then(function() {
      console.log('1');
      return doSomething1();
    })
    .then(function() {
      console.log('2');
      return doSomething2();
    })
    .then(function() {
      console.log('3');
      return doSomething3();
    })
}

考虑使用bluebird 作为承诺。与任何其他 Promise 库相比,它具有许多有用的功能。您可能会发现开始它很困难,但一旦掌握它,您就会爱上它。

【讨论】:

  • 解决所有 doSomethingN 调用后是否仍然无法正常工作?
  • 我在这里同意@Bergi。
  • @KunalKapadia:不,它按您的意图“工作”,但您的回答仍然表现出不好的做法
  • 是的@KunalKapadia,我正在寻找正确的解决方案。之前已经考虑过你的解决方案,但我因为 Bergi 所说的而放弃了它。
  • @Bergi 同意你的看法。从我的代码中消除了反模式。
猜你喜欢
  • 1970-01-01
  • 2014-04-23
  • 1970-01-01
  • 1970-01-01
  • 2017-01-04
  • 2018-05-25
  • 2014-12-27
  • 2021-10-11
  • 2018-05-09
相关资源
最近更新 更多