前几天我们在手撕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这篇文章中我们已经手把手教大家实现了resolve
和reject
两个方法,这里我们就直接接着前一篇文章的进度,不明白这两个方法怎么实现的朋友可以去看看之前的文章。
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方法,原理相同) onFulfilledCallbacks
和onRejectedCallbacks
,这样我们在调用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的提问。
有需要的小伙伴,可以点击下方卡片领取,无偿分享