theme: healer-readable
本文正在参加「金石计划 . 瓜分6万现金大奖」
前几天我们在手撕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') return
this.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') return
this.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的值,传递给下一个.then
resolve(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') return
this.status = 'rejected';
this.value = reason;
while (this.onRejectedCallbacks.length) {
this.onRejectedCallbacks.shift()(this.value)
}
}
then(onFulfilled, onRejected) {
onFulfilled = typeof onFulfilled === 'function' ? onFulfilled : val => val
onRejected = 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的值,传递给下一个.then
resolve(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的实现原理,有任何不足的之处可以在评论区留言,大家共同进步!
暂无评论内容