【问题标题】:Why is this use of Promise.finally leading to uncaught exception?为什么使用 Promise.finally 会导致未捕获的异常?
【发布时间】:2020-12-11 12:52:54
【问题描述】:

我正在实现一个名为 timeout 的函数,以将一个承诺转换为如果未在最短时间内解决则将拒绝的承诺。

通过以下实现,当传递给 timeout 的承诺在分配的时间之前被拒绝时,我会在 Chrome 开发工具中收到 uncaught (in promise) 消息。

function timeout(promise, duration, message) {
    const timeoutPromise = new Promise((_, reject) => {
        const handle = setTimeout(() => {
            reject(new TimeoutError(message ?? "Operation timed out."))
        }, duration)
        promise.finally(() => clearTimeout(handle)) //problem line
    })
    return Promise.race([promise, timeoutPromise])
}

但是,如果我将其更改为以下内容,我似乎没有问题。

function timeout(promise, duration, message) {
    const timeoutPromise = new Promise((_, reject) => {
        const handle = setTimeout(() => {
            reject(new TimeoutError(message ?? "Operation timed out."))
        }, duration)
        const clear = () => clearTimeout(handle)
        promise.then(clear).catch(clear)
    })
    return Promise.race([promise, timeoutPromise])
}

我试图了解在第二种情况下调用then 和catch 与在第一种情况下调用finally 有何不同。

【问题讨论】:

    标签: javascript ecmascript-6 es6-promise


    【解决方案1】:

    这是因为finally 处理程序不同于then 和catch 处理程序。如果 promise 被拒绝,finally 处理程序无法将拒绝更改为解决方案。就像 try/catch/finally 结构中的 finally 块一样,它的目的是在很大程度上是透明的。

    这是我在新书(第 8 章)中的描述:

    在正常情况下,finally 处理程序对通过的履行或拒绝没有影响 它(如finally 块):它返回的任何值,除了被/被拒绝的thenable 是 忽略。但是,如果它抛出错误或返回拒绝的 thenable,则该错误/拒绝将取代 任何通过它的履行或拒绝(就像在finally 块中抛出一样)。所以无法改变 一个履行值——即使它返回不同的值——但它可以将拒绝原因更改为 不同的拒绝原因,它可以将履行变为拒绝,并且可以延迟履行(通过 返回一个最终实现的thenable)。

    或者换一种说法:它就像一个不能包含return 语句的finally 块。 (它可能 有一个throw 或者可能调用一个函数或执行一些抛出的操作,但它不能返回一个新值。

    以下是无法将拒绝转换为履行的示例:

    Promise.reject(new Error("Failed"))
    .finally(() => {
        console.log("finally handler returns null");
        return null;
    });
    Look in the browser's real console to see the unhandled rejection error.

    在代码中处理它的另一种方法是代替问题行:

    promise.catch(() => {}).finally(() => clearTimeout(handle))
    

    或

    promise.catch(() => {}).then(() => clearTimeout(handle))
    

    在finally(或then)处理程序之前将拒绝转换为履行(仅在此分支上)。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2013-07-06
      • 1970-01-01
      • 1970-01-01
      • 2017-04-18
      相关资源
      最近更新 更多