
链式Promise使用finally
promise的finally
方法的行为与then
和catch
方法截然不同,
const promise = Promise.resolve(42);
promise.finally(() => {
console.log("Finally called.");
}).then(value => {
console.log(value); // 42
});
这里,settlement handler没有从promise接收到一个fullfiled值,因此该值被复制到从finally方法调用返回的新的promise中。新的promise用值42来实现,因此fullfilment handler接收42作为参数。请记住,即使返回的 Promise 和 Promise 具有相同的值,它们也不是同一个对象,如您在此示例中所见:
const promise1 = Promise.resolve(42);
const promise2 = promise1.finally(() => {
console.log("Finally called.");
});
promise2.then(value => {
console.log(value); // 42
});
console.log(promise1 === promise2); // false
在这段代码中,promise1.finally() 的返回值存储在promise2 中,此时可以确定它与promise1 不是同一个对象。finally() 的调用总是从原始promise中复制状态和值。 这也意味着当 finally() 被一个rejected的 promise 调用时,它又会返回一个rejected的 promise,如下例所示:
const promise = Promise.reject(43);
promise.finally(() => {
console.log("Finally called.");
}).catch(reason => {
console.error(reason); // 43
});
此示例中的 Promise Promise 以 43 的原因rejected。同样,settlement handler无法访问此信息,因为它没有作为参数传入,因此它返回一个新的 Promise,但由于相同的原因rejected。 然后,您可以使用catch() 来检索原因。
特殊情形
finally() 工作方式的一个例外是在settlement handler内部抛出错误或返回rejected的promise时。 在这种情况下,finally() 返回的 Promise不会保持原始 Promise 的状态和值,而是以抛出的错误为原因rejected。 这是一个例子:
const promise1 = Promise.reject(43);
promise1.finally(() => {
throw 44;
}).catch(reason => {
console.error(reason); // 44
});
const promise2 = Promise.reject(43);
promise2.finally(() => {
return Promise.reject(44);
}).catch(reason => {
console.error(reason); // 44
});
因为在此示例中settlement handler抛出 44 或返回Promise.reject(44),返回的 Promise 被拒绝,值为 44,并输出到控制台而不是 43。由于在settlement handler中抛出错误,原始promise的状态和值会丢失。
在第 1 章中,您看到了如何使用settlement handler根据对 fetch() 的调用来切换应用程序的加载状态。 使用promise链重写该示例,并混合本章前面的一些错误处理,这是一个完整的示例:
const appElement = document.getElementById("app");
const promise = fetch("books.json");
appElement.classList.add("loading");
promise.then(response => {
if (response.ok) {
console.log(response.status);
} else {
throw new Error(`Unexpected status code: ${response.status} ${response.statusText}`);
}
}).finally(() => {
appElement.classList.remove("loading");
}).catch(reason => {
console.error(reason.message);
});
与 try-catch 语句不同,您不希望 finally() 成为链的最后一部分,以防万一它抛出错误。 所以先调用 then() 来处理来自 fetch() 的响应,然后将 finally() 添加到链中以触发 UI 更改,最后 catch() 为整个链添加错误处理程序。这就是传递前一个promise状态的settlement handler有帮助的地方:如果fullfilment handler最终抛出错误,结算fullfilment handler将传递该rejection状态,以便reject handler可以访问它。