javascript基础学习系列一百六十三:Promise.prototype.finally

本文详细解释了Promise.prototype.finally()方法的作用,如何在期约解决或拒绝时执行清理代码,并探讨了其非重入性质,以及与then(),catch(),和finally()方法的异步执行区别。
摘要由CSDN通过智能技术生成

Promise.prototype.finally()方法用于给期约添加 onFinally 处理程序,这个处理程序在期约转换为解决或拒绝状态时都会执行。这个方法可以避免 onResolved 和 onRejected 处理程序中出现冗余代码。但 onFinally 处理程序没有办法知道期约的状态是解决还是拒绝,所以这个方法主要用于添加清理代码。

let p2 = Promise.reject(); 
let onFinally = function() { 
setTimeout(console.log, 0, 'Finally!') 
} 
p1.finally(onFinally); // Finally 
p2.finally(onFinally); // Finally 
Promise.prototype.finally()方法返回一个新的期约实例:
let p1 = new Promise(() => {}); 
let p2 = p1.finally();
setTimeout(console.log, 0, p1); // Promise <pending> 
setTimeout(console.log, 0, p2); // Promise <pending> 
setTimeout(console.log, 0, p1 === p2); // false 

这个新期约实例不同于 then()或 catch()方式返回的实例。因为 onFinally 被设计为一个状态无关的方法,所以在大多数情况下它将表现为父期约的传递。对于已解决状态和被拒绝状态都是如此。

// 这里都会原样后传
let p2 = p1.finally(); 
let p3 = p1.finally(() => undefined); 
let p4 = p1.finally(() => {}); 
let p5 = p1.finally(() => Promise.resolve()); 
let p6 = p1.finally(() => 'bar'); 
let p7 = p1.finally(() => Promise.resolve('bar')); 
let p8 = p1.finally(() => Error('qux')); 
setTimeout(console.log, 0, p2); // Promise <resolved>: foo 
setTimeout(console.log, 0, p3); // Promise <resolved>: foo 
setTimeout(console.log, 0, p4); // Promise <resolved>: foo 
setTimeout(console.log, 0, p5); // Promise <resolved>: foo 
setTimeout(console.log, 0, p6); // Promise <resolved>: foo 
setTimeout(console.log, 0, p7); // Promise <resolved>: foo 
setTimeout(console.log, 0, p8); // Promise <resolved>: foo 

如果返回的是一个待定的期约,或者 onFinally 处理程序抛出了错误(显式抛出或返回了一个拒绝期约),则会返回相应的期约(待定或拒绝),如下所示:

let p9 = p1.finally(() => new Promise(() => {})); 
let p10 = p1.finally(() => Promise.reject()); 
// Uncaught (in promise): undefined 
setTimeout(console.log, 0, p9); // Promise <pending> 
setTimeout(console.log, 0, p10); // Promise <rejected>: undefined 
let p11 = p1.finally(() => { throw 'baz'; }); 
// Uncaught (in promise) baz 
setTimeout(console.log, 0, p11); // Promise <rejected>: baz 

返回待定期约的情形并不常见,这是因为只要期约一解决,新期约仍然会原样后传初始的期约:

// 忽略解决的值
let p2 = p1.finally( 
 () => new Promise((resolve, reject) => setTimeout(() => resolve('bar'), 100))); 
setTimeout(console.log, 0, p2); // Promise <pending> 
setTimeout(() => setTimeout(console.log, 0, p2), 200); 
// 200 毫秒后:
// Promise <resolved>: foo

非重入期约方法
当期约进入落定状态时,与该状态相关的处理程序仅仅会被排期,而非立即执行。跟在添加这个处理程序的代码之后的同步代码一定会在处理程序之前先执行。即使期约一开始就是与附加处理程序关联的状态,执行顺序也是这样的。这个特性由 JavaScript 运行时保证,被称为“非重入”(non-reentrancy)
特性。下面的例子演示了这个特性:

let p = Promise.resolve(); 
// 添加解决处理程序
// 直觉上,这个处理程序会等期约一解决就执行
p.then(() => console.log('onResolved handler')); 
// 同步输出,证明 then()已经返回
console.log('then() returns'); 
// 实际的输出:
// then() returns 
// onResolved handler 

在这个例子中,在一个解决期约上调用 then()会把 onResolved 处理程序推进消息队列。但这个处理程序在当前线程上的同步代码执行完成前不会执行。因此,跟在 then()后面的同步代码一定先于处理程序执行。
先添加处理程序后解决期约也是一样的。如果添加处理程序后,同步代码才改变期约状态,那么处理程序仍然会基于该状态变化表现出非重入特性。下面的例子展示了即使先添加了 onResolved 处理程序,再同步调用 resolve(),处理程序也不会进入同步线程执行:

// 创建一个期约并将解决函数保存在一个局部变量中
let p = new Promise((resolve) => { 
 synchronousResolve = function() { 
 console.log('1: invoking resolve()'); 
 resolve(); 
 console.log('2: resolve() returns'); 
 }; 
}); 
p.then(() => console.log('4: then() handler executes')); 
synchronousResolve(); 
console.log('3: synchronousResolve() returns'); 
// 实际的输出:
// 1: invoking resolve() 
// 2: resolve() returns 
// 3: synchronousResolve() returns 
// 4: then() handler executes

在这个例子中,即使期约状态变化发生在添加处理程序之后,处理程序也会等到运行的消息队列让它出列时才会执行。
非重入适用于 onResolved/onRejected 处理程序、catch()处理程序和 finally()处理程序。
下面的例子演示了这些处理程序都只能异步执行:

p1.then(() => console.log('p1.then() onResolved')); 
console.log('p1.then() returns'); 
let p2 = Promise.reject(); 
p2.then(null, () => console.log('p2.then() onRejected')); 
console.log('p2.then() returns'); 
let p3 = Promise.reject(); 
p3.catch(() => console.log('p3.catch() onRejected')); 
console.log('p3.catch() returns'); 
let p4 = Promise.resolve(); 
p4.finally(() => console.log('p4.finally() onFinally')); 
console.log('p4.finally() returns');
// p1.then() returns 
// p2.then() returns 
// p3.catch() returns 
// p4.finally() returns 
// p1.then() onResolved 
// p2.then() onRejected 
// p3.catch() onRejected 
// p4.finally() onFinally
  • 9
    点赞
  • 8
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值