【问题标题】:What is the difference between then and finally in a promise?then 和 finally in a promise 有什么区别?
【发布时间】:2017-04-29 01:09:53
【问题描述】:

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

明确一点:我确切地知道为什么 thencatch 之后被调用。我希望在捕获后调用它。这就是意图。我的问题是:如果我想要代码始终被执行而不管承诺状态如何,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(...) 被拒绝的承诺。

我看到的结果是案例 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 执行“finally”代码的安全性。

所以我的问题是:thenfinally 有什么区别?它们看起来表现得一样,但我什么时候需要使用finally 而不是then

【问题讨论】:

  • 如果你总是遇到被拒绝的承诺,这意味着你只解决了承诺,那么当然没有什么区别......我错过了你的问题吗?这一切似乎都很明显..
  • 适用于承诺可以被解决或被拒绝的情况。在你的情况下,它总是得到解决,所以没有区别。
  • .catch 使它成为一个已解决的承诺..
  • @JasonC, 1st:有时您不想在错误出现的地方捕获错误,而是在使用此函数的代码中;所以你不会抓住他们。在这种情况下,您不能替换 then()finally()。有时你必须清理某事。不管有没有错误。 (清空引用清除超时,......类似的东西)这就是你使用finally()的地方。第二:你传递给catch()的函数也可以抛出,那么你会得到一个被拒绝的Promise并且不会调用下面的then()
  • 好吧,现在你不能删除它了。问题解决了!

标签: javascript node.js promise bluebird


【解决方案1】:

第一个区别:有时您不想在错误出现的地方捕获错误,而是在使用此函数的代码中,因此您不会捕获它们。在这种情况下,您不能替换 then()finally()

有时你必须清理一些东西,不管有没有错误(清空引用,清除超时......类似的东西)。这就是您使用finally() 的地方。

第二个区别:你传递给catch()的函数也可能抛出,那么你会得到一个被拒绝的Promise,并且不会调用下面的then()

(所以在 catch 之前的 finally 仍然会在错误时执行,不知道)

是的,这就是finally() 的意义所在。它会在任何情况下执行而不改变解析值。

您可能想阅读/谷歌一些关于try {} finally {} 的信息,但不要抓住。

【讨论】:

    【解决方案2】:

    .then.finally 不一样。

    .then 是主要的承诺原语。这是在 Promises/A+ spec 中彻底定义的内容,所有承诺库都将实现它。

    Bluebird .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 不会改变 promise 的解析值,也不会收到 promise 链的结果。

    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
    

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

    【讨论】:

    • 顺便说一句,如果其他人正在阅读本文,现在我已经了解了这些概念,我发现了其他一些东西,.tap() 是其中之一:tap 有点像中途在thenfinally 之间:与then 一样,它不会在拒绝时调用,但与finally 一样,它不会改变结果,因此之前的结果会“跳过”它。整洁。
    【解决方案3】:

    好吧,经过一番交谈和 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"));
    }
    

    在这些中,promise 被拒绝,但从catch 抛出错误。

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

    所以这就是的区别。它们几乎相同,唯一的例外是当 catch 处理程序抛出错误时,finally 会执行,而then 不会。

    这意味着我引用的评论也确实有优点:如果在我的错误处理程序中发生另一个错误,then 不能保证清理,但finally 可以。这就是我错过的情况。

    【讨论】:

    • 另外,.finally 不会影响为 promise 解析的值,而 .then 会。
    • @Matt,如果函数返回 undefined,则不会。但你是对的,finally() 保证解析后的值不会改变。
    • @Thomas 抱歉,我不明白您的undefined 警告?你的意思是如果你到处都返回undefined你看不到效果吗?
    • @Matt,如果您传递给then() 的函数没有返回值(又名undefined),那么JS 中Promises 的每个主要实现都不会改变解析的值,而是解析为之前的值。
    • @Thomas Native 和 Bluebird 将解析 undefined 所以我认为这是规范。 new Promise(resolve => resolve(true)).then(res => console.log('res',res)).then(res => console.log('res', res))
    猜你喜欢
    • 2014-11-19
    • 1970-01-01
    • 2018-03-12
    • 1970-01-01
    • 1970-01-01
    • 2019-04-20
    • 2013-02-27
    相关资源
    最近更新 更多