手撕Promise:Promise.then详解

前几天我们在手撕Promise 源码篇的时候,对于Promise.then 的实现只是给出了带有注解的源代码,那么让我们一步一步揭开.then的神秘面纱,如何实现.then的链式调用呢?

官方的Promise.then

let p1 = new Promise((resolve, reject) => {resolve(123); 
})
// Promise {<fulfilled>: 123}
p1.then(res => console.log(res,'success'),err => console.log(err,'failed')
)
// 123 'success'
// Promise {<fulfilled>: undefined} 

Promise.then的链式调用

let p1 = new Promise((resolve, reject) => {resolve(123); 
})
.then(res => res * 3);
.then(res => res * 3);
.then(res => {console.log(res);// 1107
}) 

从以上两个例子我们可以看到,Promise.then方法接收两个回调函数作为参数,第一个参数是fulfilled状态的回调函数,第二个参数是rejected状态的回调函数,当promise的状态为'fulfilled'会执行第一个回调函数,当状态为'rejected'时执行第二个回调函数。

这并不意味着'pending'状态的Promise对象不能在后面接.then,而是当Promsie的状态变更为'fulfilled'或者'rejected'状态的时候,才会触发里面两个回调函数其中一个,并且.then支持链式调用,链式传参,所以上一次的.then的结果会影响到下一个.then。

知道以上这些我们就可以试试来自己实现Promsie.then,在看我如何手撕Promsie这篇文章中我们已经手把手教大家实现了resolvereject两个方法,这里我们就直接接着前一篇文章的进度,不明白这两个方法怎么实现的朋友可以去看看之前的文章。

class myPromise {constructor(executor) {this.status = 'pending'; // 变更promise的状态this.value = null;try { executor(this.resolve.bind(this), this.reject.bind(this)); // new 一个myPromise 得到的实例对象里面有两个函数} catch (error) { this.reject(error) } }resolve(value) {if (this.status !== 'pending') return;this.status = 'fulfilled'; // 变更promise的状态this.value = value;}reject(reason) {if (this.status !== 'pending') returnthis.status = 'rejected';this.value = reason;}
} 

首先.then返回的也是一个Promise对象,所以我们第一步

then(onFulfilled, onRejected) {return new myPromise((resolve, reject) => {} 

第二步.then的执行虽然不受我们控制,但是其内部的回调的执行取决于'Promise的状态',只要Promise的状态处于 'pending' 状态,就算后面接了再多的.then都不会执行,所以我们要将成功或者失败的回调都使用两个队列存起来,等到Promise的状态变更的时候再根据队列先进先出的特性,依次将队列中的回调执行。

我们在构造器函数中定义了两个队列 (只使用push和shift方法,即可人为认定队列,或者使用pop和unshift方法,原理相同) onFulfilledCallbacksonRejectedCallbacks,这样我们在调用resolve或者reject方法的时候,只需要判断,相应的回调函数队列中是否有未执行的函数,使用shift方法切出来之后执行。

 constructor(executor) {this.status = 'pending'; this.value = null;this.onFulfilledCallbacks = []; // 用来保存成功的回调(处理异步)this.onRejectedCallbacks = []; // 用来保存失败的回调(处理异步)try {executor(this.resolve.bind(this), this.reject.bind(this));} catch (error) {this.reject(error)}}resolve(value) {if (this.status !== 'pending') return;this.status = 'fulfilled';this.value = value;// 调用then里面的回调while (this.onFulfilledCallbacks.length) {// 判断是否存在未执行的回调函数this.onFulfilledCallbacks.shift()(this.value)// 存在则切出来执行掉}}reject(reason) {if (this.status !== 'pending') returnthis.status = 'rejected';this.value = reason;while (this.onRejectedCallbacks.length) {// 判断是否存在未执行的回调函数this.onRejectedCallbacks.shift()(this.value)// 存在则切出来执行掉}} 

第三步,在.then中我们需要判断Promise的状态,当状态为'fulfilled'则调用resolve方法,状态为'rejected'则调用'reject'方法,如果状态为'pending'那就将回调存到对应的回调队列中去。

因为不能将Promise对象本身作为回调参数,所以我们将要返回的Promise对象声明一个变量,以便于判断是否将自身作为回调传进来,如果将自身作为回调我们就是抛出一个错误,如果参数就是一个新的Promise对象,则直接调用.then

then(onFulfilled, onRejected) { var thenPromise = new myPromise((resolve, reject) => {const resolvePromise = callback => {try {const x = callback(this.value);// 声明回调的调用if (x === thenPromise) {// 你正在返回自身throw new Error('不允许返回自身!');}if (x instanceof myPromise) { // 返回的是一个Promise对象x.then(resolve, reject);} else { // 直接返回一个值,作为resolve的值,传递给下一个.thenresolve(x);}} catch (error) {reject(error);throw new Error(error);}})}if (this.status === 'fulfilled') {resolvePromise(onFulfilled)} else if (this.status === 'rejected') {resolvePromise(onRejected)} else if (this.status === 'pending') {this.onFulfilledCallbacks.push(resolvePromise.bind(this, onFulfilled));// 考虑this的指向问题,直接使用bind方法显示绑定this.onRejectedCallbacks.push(resolvePromise.bind(this, onRejected));// 考虑this的指向问题,直接使用bind方法显示绑定}})return thenPromise} 

最后一步,我们要知道Promise.then是属于异步代码中的微任务事件,其执行顺序会在宏任务之后执行,但是我们目前实现的效果是.then这一步的逻辑还是同步代码,所以我们在返回的Promise对象的外面套一层setTimeout,这样返回出去的.then的逻辑,会去到下一次的宏任务队列,这样就实现了.then的执行会比同步代码稍晚一些,但是官方并不是使用setTimeout实现的。

完整版then方法代码:

class myPromise {constructor(executor) {this.status = 'pending'; this.value = null;this.onFulfilledCallbacks = []; // 用来保存成功的回调(处理异步)this.onRejectedCallbacks = []; // 用来保存失败的回调(处理异步)try {executor(this.resolve.bind(this), this.reject.bind(this));} catch (error) {this.reject(error)}}resolve(value) {if (this.status !== 'pending') return;this.status = 'fulfilled';this.value = value;// 调用then里面的回调while (this.onFulfilledCallbacks.length) {this.onFulfilledCallbacks.shift()(this.value)}}reject(reason) {if (this.status !== 'pending') returnthis.status = 'rejected';this.value = reason;while (this.onRejectedCallbacks.length) {this.onRejectedCallbacks.shift()(this.value)}}then(onFulfilled, onRejected) {onFulfilled = typeof onFulfilled === 'function' ? onFulfilled : val => valonRejected = typeof onRejected === 'function' ? onRejected : val => { throw val }var thenPromise = new myPromise((resolve, reject) => {const resolvePromise = callback => {setTimeout(() => { // 让整个回调函数比同步代码晚一点执行,官方不是使用setTimeout实现try {const x = callback(this.value);if (x === thenPromise) {// 你正在返回自身throw new Error('不允许返回自身!');}if (x instanceof myPromise) { // 返回的是一个Promise对象x.then(resolve, reject);} else { // 直接返回一个值,作为resolve的值,传递给下一个.thenresolve(x);}} catch (error) {reject(error);throw new Error(error);}})}if (this.status === 'fulfilled') {resolvePromise(onFulfilled)} else if (this.status === 'rejected') {resolvePromise(onRejected)} else if (this.status === 'pending') {this.onFulfilledCallbacks.push(resolvePromise.bind(this, onFulfilled));this.onRejectedCallbacks.push(resolvePromise.bind(this, onRejected));}})return thenPromise}
} 

让我们看看最终的效果:

希望这篇文章能够帮助到大家去更好的理解.then的实现原理,有任何不足的之处可以在评论区留言,大家共同进步!

最后

整理了75个JS高频面试题,并给出了答案和解析,基本上可以保证你能应付面试官关于JS的提问。



有需要的小伙伴,可以点击下方卡片领取,无偿分享

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值