My Promise
前言
近期,自己打算更换一个状态/工作,于是开始对堆积已久的前端手写面试题进行复习;想了想,感觉Promise自己应该只算熟悉,不算精通,只停留会用阶段,因此打算今日好好将其抽丝剥茧,掌握它!
注:在实现的过程需要了解Promise的规范,
- Promise A+(promisesaplus.com/)
- 中文版:tsejx.github.io/javascript-...
作用
想要实现Promise,首先得了解Promise的作用是什么? MDN解释:Promise
是一个对象,它代表了一个异步操作的最终完成或者失败。
什么意思呢?意思是,Promise只是一个对象,使用者可以在Promise的状态决定之后通过链式/(async/await)的方式来获取到异步的结果,避免掉回调地狱(在有需要到上一个异步请求返回值的时候)。
状态
需了解Promise的状态才能够实现Promise,Promise的状态分别为:Pending(等待)、Fulfilled(完成)、Reject(拒绝)。
注:Promise的状态一旦确定之后,无法再更改。
值
除了了解Promise的状态外,还需对其值有所谓了解,Promise的值共有:
- status(当前Promise实例的状态,一经确认无法改变)
- reason(失败原因,reject函数携带回来给catch函数使用)
- value(最终resolve函数给then函数的第一个函数使用)
- onFulfilledFnList(存储then函数传递的第一个函数,后续在resolve执行时调用)
- onRejectedFnList(存储then函数传递的第二个函数/catch传递的函数,后续要reject执行时调用)
建立
首先,先构建一个类,因为Promise都是new出来的,然后需要往constructor传入一个立即执行函数;立即执行函数中还会接受两个回调,分别是resolve,reject;
- resolve:当在立即执行函数中调用时,则表示当前的Promise状态为Fulfilled(完成);
- reject:当在立即函数中调用时,则表示当前的promise状态reject(失败);
js
const PromiseStatus = {
PENDING: "pending",
FULFILLED: "fulfilled",
REJECTED: "rejected",
};
class MyPromise {
constructor(executor) {
// 状态
this.status = PromiseStatus.PENDING;
// 最终值
this.value = undefined;
// 错误值
this.reason = undefined;
// 成功回调队列
this.onFulfilledFnList = [];
// 失败回调队列
this.onRejectedFnList = [];
// resolve
const resolve = (val) => {
...
};
// reject
const reject = (err) => {
...
};
executor(resolve, reject);
}
}
const p = new MyPromise((resolve, reject) => {
resolve(111);
});
resolve的实现
resolve函数主要的功能为改变Promise状态,存储携带过来的结果 ,执行当前已经存储的onFulFilledFnL。
注:在处理时,需判断当前的状态是否是pending,如果不是pending,则不再处理任何操作。
js
const resolve = (val) => {
// 如果当前状态已改变,不再处理
if (this.status !== PromiseStatus.PENDING) {
return;
}
this.value = val;
this.status = PromiseStatus.FULFILLED;
this.onFulfilledFnList.forEach((f) => f());
};
const reject = (err) => {
// 如果当前状态已改变,不再处理
if (this.status !== PromiseStatus.PENDING) {
return;
}
this.reason = err;
this.status = PromiseStatus.REJECTED;
this.onRejectedFnList(() => f());
};
注:reject函数同理,则不再过多叙述。
then方法
then方法是整个promise中最重要的一环,贯穿全局;then方法接受两个参数,分别是onFulFilled、onRejected(则resolve/reject执行后调用的函数),且它的返回值为一个新的promise,并将resolve/reject的结果带到下一个promise的then中。
- 注册函数:then方法接受的两个参数,都需要进行判断类型,如果不是函数类型,则添加对应的默认值。注:onRejected添加的默认值中,需要throw error。
- 返回新的promise,在新的promise中,需要去判定当前promise的状态,如果状态是pending,则需要onFulfilled/onRejected添加到对应的队列中;如果是Fulfilled,则直接执行onFulfilled方法,将其返回值给新的promise的resolve函数,反之Rejected同理。
js
then(fulfilled, rejected) {
const onFulfilled = typeof fulfilled === "function" ? fulfilled : () => {};
const onRejected =
typeof rejected === "function"
? rejected
: (err) => {
throw Error(err);
};
const promise = new MyPromise((resolve, reject) => {
// 判断状态,如果是pending,则分别将onFulfilled,onReject添加到对应的队列中
if (this.status === PromiseStatus.PENDING) {
try {
this.onFulfilledFnList.push(() => {
queueMicrotask(() => {
try {
const res = onFulfilled(this.value);
this.resolvePromise(promise, res, resolve, reject);
} catch (error) {
reject(error);
}
});
});
this.onRejectedFnList.push(() => {
queueMicrotask(() => {
try {
const err = onRejected(this.reason);
this.resolvePromise(promise, err, resolve, reject);
} catch (error) {
reject(error);
}
});
});
} catch (error) {
reject(error);
}
}
if (this.status === PromiseStatus.FULFILLED) {
queueMicrotask(() => {
try {
const res = onFulfilled(this.value);
this.resolvePromise(promise, res, resolve, reject);
} catch (error) {
reject(error);
}
});
}
if (this.status === PromiseStatus.REJECTED) {
queueMicrotask(() => {
try {
const err = onRejected(this.reason);
this.resolvePromise(promise, err, resolve, reject);
} catch (error) {
reject(error);
}
});
}
});
return promise;
}
resolve的返回值
在对应的规范中,resolve的返回值分别有以下规定:
- 规则一:如果当前的promise与res返回值一致,则报错,不允许返回相同的promise
- 规则二:如果当前的res是promise,则直接调用res的then方法
- 规则三:如果res是一个对象,且拥有then函数,则调用该res的then函数
- 规则四:如果都不属于,直接resolve对应值
js
resolvePromise(promise, res, resolve, reject) {
// 规则一:如果当前的promise与res返回值一致,则报错,不允许返回相同的promise
if (promise === res) {
return reject(new Error("Chaining cycle detected for promise"));
}
// 规则二:如果当前的res是promise,则直接调用res的then方法
if (res instanceof MyPromise) {
res.then(resolve, reject);
return;
}
// 规则三:如果res是一个对象,且拥有then函数,则调用该res的then函数
if (typeof res === 'object' && typeof res.then === 'function') {
try {
res.then(resolve, reject);
} catch (error) {
reject(error);
}
return;
}
// 规则四:如果都不属于,直接resolve对应值
resolve(res);
}
catch方法
直接调用then方法即可,onFulfilled方法无法传递,只传递onRejected即可。
js
catch(onRejected) {
this.then(null, onRejected);
}
finally方法
注意点:
- 无论状态是fulfilled或者rejected,都需要执行该方法,且后续还可以通过链式继续调用
- 实现的过程需要忽略finally方法的返回值,只需传递上一个promise的值即可
js
finally(callback) {
return this.then(
(value) => MyPromise.resolve(callback()).then(() => value),
(err) => MyPromise.reject(callback()).catch(() => { throw err; })
)
}
static resolve方法
注意点:需要判断当前传入的值是否为promise,如果不是,需进行包括才能返回,如果是,直接返回
js
static resolve(value) {
if(value instanceof MyPromise) {
return value;
}
return new MyPromise((resolve) => resolve(value));
}