带你手写promise的整个逻辑
想要手写一个promise,首先就要了解promise,想必大家都被问过一些promise的面试题,知道一些promise的用法,主要考的就是一种异步编程的思想。
本篇文章主要实现promise的then、catch、finally方法及链式调用和值穿透等。通过推导的方式实现较为完整的Promise。
Promise-结构的设计
promise的特性
- 创建promise实例时,传入的回调函数(executer)会被同步执行
- promise存在三种状态,分别是pending、fulfilled、rejected
- pending(等待态)为初始态,成功时会转化为fulfilled,失败时会转化rejected,状态一旦决议就无法改变
- 如果传入的executor报错,就会直接执行reject
// 定义三种状态的常量
const PROMISE_STATUS_PENDING = "pending"; // 等待状态
const PROMISE_STATUS_FULFILLED = "fulfilled"; // 成功状态
const PROMISE_STATUS_REJECTED = "rejected"; // 失败状态
class MYPromise {
constructor(executer) {
this.status = PROMISE_STATUS_PENDING; // 初始态为pending
this.value = undefined; // 成功的值
this.reason = undefined; // 失败的值
const resolve = (value) => {
if (this.status === PROMISE_STATUS_PENDING) {
this.status = PROMISE_STATUS_FULFILLED; // 确认成功状态
this.value = value;
}
};
const reject = (reason) => {
if (this.status === PROMISE_STATUS_PENDING) {
this.status = PROMISE_STATUS_REJECTED; // 确认失败状态
this.reason = reason;
}
};
try {
executer(resolve, reject); // 创建实例时直接执行
} catch (error) {
reject(error); // 如果传入的executor报错,就会直接执行reject
}
}
}
Promise-then方法
then方法的基本实现
promise中有一个then方法,里面有两个参数:onFulfilled(成功的回调)和onRejected(失败的回调)
- 当状态为fulfilled时,将this.value的值传入onFulfilled执行。当状态为rejected时,将this.reason的值传入onRejected执行
- 由于onFulfilled和和onRejected要等this.value和this.reason有值了再执行,所以resolve函数和reject函数内部应该异步执行,我这边使用queueMicrotask(原生api,传入的回调函数会当作微任务执行)
- 由于promise实例在多处都会使用,所以可以通过数组对onFulfilled和和onRejected进行保存
const PROMISE_STATUS_PENDING = "pending";
const PROMISE_STATUS_FULFILLED = "fulfilled";
const PROMISE_STATUS_REJECTED = "rejected";
class MYPromise {
constructor(executer) {
this.status = PROMISE_STATUS_PENDING;
this.value = undefined;
this.reason = undefined;
this.onFulfilledFns = []; // 对onFulfilled进行保存
this.onRejectedFns = []; // 对onRejected进行保存
const resolve = (value) => {
if (this.status === PROMISE_STATUS_PENDING) {
queueMicrotask(() => { // 先将成功的回调存入this.onFulfilledFns中再挨个执行,使用微任务的方式
this.status = PROMISE_STATUS_FULFILLED;
this.value = value;
this.onFulfilledFns.forEach((fn) => fn(value));
});
}
};
const reject = (reason) => {
if (this.status === PROMISE_STATUS_PENDING) {
queueMicrotask(() => { // 先将失败的回调存入this.onRejectedFns中再挨个执行,使用微任务的方式
this.status = PROMISE_STATUS_REJECTED;
this.reason = reason;
this.onRejectedFns.forEach((fn) => fn(reason));
});
}
};
try {
executer(resolve, reject);
} catch (error) {
reject(error);
}
}
then(onFulfilled, onRejected) {
this.onFulfilledFns.push(onFulfilled); // 存入onFulfilledFns
this.onRejectedFns.push(onRejected); // 存入onFulfilledFns
}
}
解决在宏任务中执行promise.then的问题
上面已经完成对.then方法的基本实现,但是会遇到一个问题。
问题:如果把promise放到setTimeout中执行,由于宏任务会等微任务执行完后再执行,那么.then方法中的回调函数并不会加入到this.onFulfilledFns和this.onRejectedFns中。
解决方法:由于在setTimeout中状态已经决议完成,那么可以通过判断状态如果是fulfilled就执行成功的回调,是rejected就执行失败的回调,无须存在数组中。
then(onFulfilled, onRejected) {
if (this.status === PROMISE_STATUS_FULFILLED) { // 如果是fulfilled就执行成功的回调(考虑setTimeout)
onFulfilled(this.value);
}
if (this.status === PROMISE_STATUS_REJECTED) { // 如果rejected就执行失败的回调(考虑setTimeout)
onRejected(this.reason);
}
if (this.status === PROMISE_STATUS_PENDING) {
this.onFulfilledFns.push(onFulfilled); // 存入onFulfilledFns
this.onRejectedFns.push(onRejected); // 存入onFulfilledFns
}
}
实现promise的链式调用及值穿透
在开发中我们经常用到new Promise().then().then()
,这就是链式调用,用于解决回调地狱
- 为了达成链式,可以将then里的返回值为promise,通过resolve和reject值穿透到下一个promise中。
then(onFulfilled, onRejected) {
return new MYPromise((resolve, reject) => {
// 返回promise实现链式
if (this.status === PROMISE_STATUS_FULFILLED) {
try {
const value = onFulfilled(this.value); // 拿到返回值
resolve(value); // 传递到下一个promise中,下同
} catch (error) {
reject(error);
}
}
if (this.status === PROMISE_STATUS_REJECTED) {
try {
const reason = onRejected(this.reason);
reject(reason);
} catch (error) {
reject(error);
}
}
if (this.status === PROMISE_STATUS_PENDING) {
if (onFulfilled) {
this.onFulfilledFns.push(() => {
// 通过回调的方式存在数组中,以实现值传递,下同
try {
const value = onFulfilled(this.value);
resolve(value);
} catch (error) {
reject(error);
}
});
}
if (onRejected) {
this.onRejectedFns.push(() => {
try {
const reason = onRejected(this.reason);
reject(reason);
} catch (error) {
reject(error);
}
});
}
}
});
}
Promise-catch方法
Promise
对象的 catch()
方法用于注册一个在 promise 被拒绝时调用的函数。它会立即返回一个等价的 Promise
对象,这可以允许你链式调用其他 promise 的方法。
实现思路:
catch()
方法内部会调用当前 promise 对象的then()
方法,并将undefined
和onRejected
作为参数传递给then()
- 如果then中没有传入
onRejected
,那么默认给onRejected
一个抛异常的函数,以传递到下一个catch
中
then(onFulfilled, onRejected) {
// 如果onRejected没有传入,就将抛异常给catch中
onRejected =
onRejected ||
((err) => {
throw err;
});
return new MYPromise((resolve, reject) => {
......
});
}
catch(onRejected) {
const reason = this.then(undefined, onRejected);
return reason;
}
Promise-finally方法
finally()
方法返回一个 Promise
。在 promise 结束时,无论结果是 fulfilled 或者是 rejected,都会执行指定的回调函数。
实现思路:
finally
方法内部调用promise对象的then()
方法,并将onFinally
作为成功和失败回调参数传递给then(),.then(onFinally, onFinally)
- 但是会遇到一个问题,如果执行完then方法是fulfilled会到catch中,由于catch中onfulfilled是undefined,所以会导致断层。这边解决方案是如果onFulfilled没有值,就默认给一个返回值和参数一样的函数。
then(onFulfilled, onRejected) {
onRejected =
onRejected ||
((err) => {
throw err;
});
// 如果是fulfilled执行完then方法,会到catch中,但是为了避免断层(因为catch中onfulfilled是undefined)
onFulfilled =
onFulfilled ||
((value) => {
return value;
});
return new MYPromise((resolve, reject) => {
......
});
}
catch(onRejected) {
const reason = this.then(undefined, onRejected);
return reason;
}
finally(onFinally) {
this.then(onFinally, onFinally);
}
完整代码
// 定义三种状态的常量
const PROMISE_STATUS_PENDING = "pending";
const PROMISE_STATUS_FULFILLED = "fulfilled";
const PROMISE_STATUS_REJECTED = "rejected";
class MYPromise {
constructor(executor) {
this.status = PROMISE_STATUS_PENDING;
this.value = undefined;
this.reason = undefined;
// 为了支持promise多次使用,将成功和失败的回调保存到[]中
this.onFulfilledFns = [];
this.onRejectedFns = [];
const resolve = (value) => {
if (this.status === PROMISE_STATUS_PENDING) {
// 放到微任务,确保可以拿到onFulfilled
queueMicrotask(() => {
if (this.status !== PROMISE_STATUS_PENDING) return;
this.status = PROMISE_STATUS_FULFILLED;
this.value = value;
this.onFulfilledFns.forEach((fn) => fn(value));
});
}
};
const reject = (reason) => {
if (this.status === PROMISE_STATUS_PENDING) {
// 放到微任务,确保可以拿到onRejected
queueMicrotask(() => {
if (this.status !== PROMISE_STATUS_PENDING) return;
this.status = PROMISE_STATUS_REJECTED;
this.reason = reason;
this.onRejectedFns.forEach((fn) => fn(reason));
});
}
};
try {
executor(resolve, reject); // 执行回调函数
} catch (err) {
reject(err);
}
}
then(onFulfilled, onRejected) {
// 如果onRejected没有传入,就将抛异常给catch中
onRejected =
onRejected ||
((err) => {
throw err;
});
// 如果是fulfilled执行完then方法,会到catch中,但是为了避免断层(因为catch中onfulfilled是undefined)
onFulfilled =
onFulfilled ||
((value) => {
return value;
});
// 返回promise为了能够达到链式调用与值穿透
return new MYPromise((resolve, reject) => {
// 异步使用promise的判断
if (this.status === PROMISE_STATUS_FULFILLED) {
try {
const value = onFulfilled(this.value); // 拿到返回值
resolve(value); // 传递到下一个promise中,下同
} catch (error) {
reject(error);
}
}
if (this.status === PROMISE_STATUS_REJECTED) {
try {
const reason = onRejected(this.reason);
reject(reason);
} catch (error) {
reject(error);
}
}
// 同步使用promise的判断:成功的回调和失败的回调保存到数组中
if (this.status === PROMISE_STATUS_PENDING) {
// 通过push回调函数的方式,实现同步promise的值穿透及链式调用
if (onFulfilled) {
this.onFulfilledFns.push(() => {
try {
const value = onFulfilled(this.value);
resolve(value);
} catch (error) {
reject(error);
}
});
}
if (onRejected) {
this.onRejectedFns.push(() => {
try {
const reason = onRejected(this.reason);
reject(reason);
} catch (error) {
reject(error);
}
});
}
}
});
}
catch(onRejected) {
const reason = this.then(undefined, onRejected);
return reason;
}
finally(onFinally) {
this.then(onFinally, onFinally);
}
}
结尾
本文手写promise的内容,参考大神coderwhy老师讲解的手写promise的整个逻辑。
转载自:https://juejin.cn/post/7222310732293570619