在承诺中,当时和最后有什么区别?

时间:2016-12-13 20:39:24

标签: javascript node.js promise bluebird

我看到Bluebird的finally文档,但我仍然不太了解与then的区别。

要明确:我确切知道then之后调用catch的确切原因。我希望在捕获后调用它。这是意图。我的问题是:如果我希望代码始终执行而不管承诺状态如何,那么thenfinally之间的区别是什么?

我构建了这个测试:

var Promise = require("bluebird");

function test1 () {
    console.log("RESOLVE + THEN + CATCH + THEN");
    return new Promise((resolve, reject) => resolve())
       .then(() => console.log("then"))
       .catch(err => console.log("error:", err.message))
       .then(() => console.log("end"));
}

function test2 () {
    console.log("REJECT + THEN + CATCH + THEN");
    return new Promise((resolve, reject) => reject(new Error("rejected")))
       .then(() => console.log("then"))
       .catch(err => console.log("error:", err.message))
       .then(() => console.log("end"));
}

function test3 () {
    console.log("RESOLVE + THEN + CATCH + FINALLY");
    return new Promise((resolve, reject) => resolve())
       .then(() => console.log("then"))
       .catch(err => console.log("error:", err.message))
       .finally(() => console.log("end"));
}

function test4 () {
    console.log("REJECT + THEN + CATCH + FINALLY");
    return new Promise((resolve, reject) => reject(new Error("rejected")))
       .then(() => console.log("then"))
       .catch(err => console.log("error:", err.message))
       .finally(() => console.log("end"));
}

// run tests "sequentially" so console output doesn't get blended
setTimeout(test1, 500);
setTimeout(test2, 1000);
setTimeout(test3, 1500);
setTimeout(test4, 2000);

这测试了四种情况:

  1. .then(...).catch(...).then(...)已经解决了承诺。
  2. .then(...).catch(...).then(...)拒绝承诺。
  3. .then(...).catch(...).finally(...)已经解决了承诺。
  4. .then(...).catch(...).finally(...)拒绝承诺。
  5. 我看到的结果是案例1 + 2的行为与3 + 4相同:最后一位(thenfinally取决于测试)执行无论发生在它之前发生了什么,如预期的那样。该计划的输出是:

    RESOLVE + THEN + CATCH + THEN
    then
    end
    REJECT + THEN + CATCH + THEN
    error: rejected
    end
    RESOLVE + THEN + CATCH + FINALLY
    then
    end
    REJECT + THEN + CATCH + FINALLY
    error: rejected
    end
    

    现在,我问的原因是因为我看到了comment on this other question I asked

      

    不确定您的承诺是否支持,但您应将最后.then更改为.finally,以便busy始终被清除。

    根据我对then的非常有限的了解以及上面的测试,似乎then就足够了。但在那次评论之后,我正在质疑自己以及使用then执行“最终”代码的安全性。

    所以我的问题是:thenfinally之间有什么区别?他们看起来就像他们的行为一样,但我什么时候需要使用finally代替then

3 个答案:

答案 0 :(得分:8)

第一个区别:有时你不想在它们出现的地方发现错误,但是在使用这个功能的代码中,所以你不会抓住它们。在这种情况下,您无法替换then()finally()

有时你必须清理一些事情是否有错误(归零引用,清除超时......这样的东西)。您使用finally()的地方。

第二个区别:你传递给catch()的函数也可能抛出,然后你会被拒绝的Promise,并且不会调用以下的then()

  

(所以最后一个捕获仍然会在错误上执行,并不知道)

是的,这是finally()的重点。它将在任何情况下执行,而不会更改已解决的值。

你可能想要阅读/谷歌一点try {} finally {},没有捕获。

答案 1 :(得分:4)

.then.finally不一样。

.then是主要的promise原语。它是Promises/A+ spec中完全定义的内容,所有承诺库都会实现它。

无论承诺的命运如何,都会召唤一只蓝鸟.finally处理程序"因此,未处理的异常仍会触发.finally

new Promise((resolve, reject) => reject(false))
  .finally(a => console.log('finally', a))
// finally undefined
// Unhandled rejection false

new Promise((resolve, reject) => reject(false))
  .then(a => console.log('then', a))
// Unhandled rejection false

.finally不会更改承诺的已解决价值,也不会收到承诺链的结果。

new Promise((resolve, reject) => reject(false))
  .catch(e => {
    console.log(e)
    return 2
  })
  .finally(a => {
    console.log('finally', a)
    return 1
  })
  .then(res => console.log('res', res))
// finally undefined
// res 2

这些方法在测试用例中看起来类似,因为测试会捕获所有错误,并且您只使用promises进行流控制,而不依赖于在promise链中解析/拒绝的值。

答案 2 :(得分:1)

好吧,经过KevinB的一些聊天和很多帮助,我发现至少有一个区别。考虑以下两个新测试:

function test5 () {
    console.log("REJECT + THEN + CATCH/THROW + THEN");
    return new Promise((resolve, reject) => reject(new Error("rejected")))
       .then(() => console.log("then"))
       .catch(function(err) { throw new Error("error in catch"); })
       .then(() => console.log("end"));
}

function test6 () {
    console.log("REJECT + THEN + CATCH/THROW + FINALLY");
    return new Promise((resolve, reject) => reject(new Error("rejected")))
       .then(() => console.log("then"))
       .catch(function(err) { throw new Error("error in catch"); })
       .finally(() => console.log("end"));
}

在这些承诺中,承诺被拒绝,但catch会引发错误。

最终在两种情况下都会拒绝承诺,但对于finally案例finally仍然执行,then不会。

他们几乎相同,但唯一的例外是,当 catch 处理程序发出错误时,finally会执行,{ {1}}没有。

这意味着我引用的评论也有其优点:如果在我的错误处理程序中发生了另一个错误,then将无法保证清理,但then会发生错误。这就是我失踪的情况。