promise catch:如何知道错误是来自 promise 拒绝还是来自 then 语句

promise catch: how to know if the error comes from promise rejection or from the then statement

想象一个 returns 承诺的函数(例如从 fetch API 中获取):

fetch(...)
.then((response) => {...})
.catch((error) => {...});

我如何知道 catch 语句中的错误是否源于 promise 拒绝而不是 then 语句中的代码?

像下面这样的东西会起作用:

let success = false;
fetch(...)
.then((response) => {success = true; ...})
.catch((error) => {if(success) ...});

但我想知道是否有更好或更原生的方法来做到这一点。我也尝试过做类似的事情:

fetch(...)
.catch((promiseError) => {...});
.then((response) => {...})
.catch((thenError) => {...});

但我认为它不起作用,因为 response 没有转发到 then 声明(或者至少 TS 是这么说的)。

哪种方法更好?

如果函数传递给 then returns 一个承诺,

promise
    .then(
        (async (response)=>{
            ...
        }).catch((errorFromThen) => {})
    ).catch((errorFromPromise) => {})

或者只是简单地

promise
    .then(
        (response)=>{
            try {
                ...
            } catch (errorFromThen) {
                ...
            }
        }
    ).catch((errorFromPromise) => {})

then 接受两个参数¹:一个调用实现的函数,另一个调用拒绝的函数。所以你可以更早地处理最初的拒绝:

fetch(/*...*/)
.then(
    // Fulfillment handler
    (response) => {
        /*...*/
    },
    // Rejection handler for `fetch` promise
    (error) => {
        /*...*/
    }
)
.catch((error) => {
    // This rejection handler only sees errors not handled above
});

第一个拒绝处理程序(在 then 调用中)仅在拒绝来自 fetch 的承诺时被调用,而不是因履行处理程序中的错误而导致的拒绝。

第二个拒绝处理程序(在 catch 调用中)被调用以处理由它之前的处理程序(实现或拒绝)的错误(或拒绝的承诺)引起的拒绝;如果原始承诺被拒绝,它不会被调用(但如果第一个拒绝处理程序抛出错误或 return 承诺 is/will 被拒绝,它可能会被调用)。

所以在那些你关心的情况下,你可以更接近源地处理它。

注意所有不抛出错误或return承诺is/will被拒绝的拒绝处理程序转换拒绝(原始的承诺)转化为实现(来自 then/catch 的)。这对下游 then 处理程序很重要。


理解这一点的关键是要记住 then(及其包装器 catchfinallyreturn 一个新的承诺。这就是为什么:

aPromise.then(onFulfilled).catch(onRejected);

不同
aPromise.then(onFulfilled, onRejected);

第一个将履行处理程序连接到 aPromise,然后将拒绝处理程序连接到承诺 then returns(这意味着拒绝处理程序将被调用如果 aPromise 拒绝 如果履行处理程序抛出错误或 return 拒绝承诺)。相反,第二个仅在 aPromise 上挂接处理程序,因此来自履行处理程序的 errors/rejections 不会触发拒绝处理程序。


¹ 事实上,.catch(fn) 只是 .then(undefined, fn) 的包装。 :-) spec link