看了很多版本的Promise手写源码,感觉都差那么点儿意思,直到看到抖音某一位大佬对Promise的阐述,发现理解的真深刻,于是自己动手捋清楚这里面的逻辑,整理成笔记如下
Promise最核心的其实就是两个地方:一个是其构造函数constructor;一个是其里面的then方法,搞定这两个核心问题,其他的都是细枝末节。解决这两个问题,其他的都呼之欲出了。
1、MyPromise的构造函数
Promise的用法
const p = new Promise((resolve, reject) => {
resolve(1);
})
其作为构造函数通过new调用,传入的参数其实是一个函数,姑且称之为执行器-executor,而且这个执行器也接受两个实参resolve和reject,这两个参数也是一个函数,调用这两个函数其中一个就可以改变Promise的内部状态,一旦状态固定,就不会再改变了
构造函数第一版
const PENDING = "pending";
const FUIFILLED = "fulfilled";
const REJECTED = "rejected";
class MyPromise {
#state = PENDING;
#result = undefined;
constructor(executor) {
const resolve = data => {
if (this.#state !== PENDING) return;
this.#state = FUIFILLED;
this.#result = data;
};
const reject = reason => {
if (this.#state !== PENDING) return;
this.#state = REJECTED;
this.#result = reason;
};
try {
executor(resolve, reject);
} catch (error) {
reject(error);
}
}
}
- 这里用三个常量表示MyPromise的内部状态,用ES6类中的私有属性定义状态和结果两个属性,这样外部就访问不到,起到保护作用
- try...catch用来处理执行器同步函数的错误处理,异步错误是处理不到的
- resolve和reject两个函数中需要先判断状态,一旦状态发生变化就返回,而且这里是有重复代码的,可以提取出一个#changeState函数
构造函数第二版
const PENDING = "pending";
const FUIFILLED = "fulfilled";
const REJECTED = "rejected";
class MyPromise {
#state = PENDING;
#result = undefined;
constructor(executor) {
const resolve = data => {
this.#changeState(FUIFILLED, data);
};
const reject = reason => {
this.#changeState(REJECTED, reason);
};
try {
executor(resolve, reject);
} catch (error) {
reject(error);
}
}
/**
* 状态改变函数
* @param {string} state 内部状态
* @param {any} result 内部结果
* @returns {void} void
*/
#changeState(state, result) {
if (this.#state !== PENDING) return;
this.#state = state;
this.#result = result;
}
}
2、MyPromise.prototype.then方法
then函数的签名
then(onFulfilled, onRejected){
return new MyPromise((resolve,reject)=>{})
}
其实then方法主要有解决两个问题
- 第一个是onFulfilled和onRejected两个函数什么时候运行
- 第二个是返回的新的Promise中的状态什么时候变化即resolve和reject的执行时机
第一个问题:传入的两个回调函数参数onFulfilled和onRejected什么时候执行?
对于第一个问题可以用下图表示:
/**
* 执行handlers
* @returns {void} void
*/
#run() {
if (this.#state === PENDING) return;
// 循环遍历
while (this.#handlers.length) {
const { onFulfilled, onRejected, resolve, reject } = this.#handlers.shift();
if (this.#state === FUIFILLED) {
if (typeof onFulfilled === "function") {
onFulfilled(this.#result);
}
} else {
if (typeof onRejected === "function") {
onRejected(this.#result);
}
}
}
}
/**
* 主要记录#handlers、执行#run
* @param {Function} onFulfilled 成功回调
* @param {Function} onRejected 失败回调
* @returns {MyPromise} 返回一个新的MyPromise
*/
then(onFulfilled, onRejected) {
return new MyPromise((resolve, reject) => {
this.#handlers.push({ onFulfilled, onRejected, resolve, reject });
this.#run();
});
}
- 如上图所示,then方法所做的事情无非就两件事,第一是记录#handlers数组;第二就是执行#run方法
- #handlers数组需要在构造函数里定义下,存储一个对象数组
- #run方法就是在内部状态发生变化时,执行onFulfilled和onRejected两个回调函数
到目前为止,第一个问题大致解决了。
第二个问题:返回新的Promise中的resolve和reject什么时候运行,内部的状态是怎么变化的?
对于第二个问题,有三种情况如下:
- 传入的onFulfilled和onRejected不是一个函数
- 传入的回调是一个函数
- 回调函数的返回结果是一个Promise
当传入的onFulfilled和onRejected不是一个函数
这里要具有穿透性
/**
* 执行handlers
* @returns {void} void
*/
#run() {
if (this.#state === PENDING) return;
// 循环遍历
while (this.#handlers.length) {
const { onFulfilled, onRejected, resolve, reject } = this.#handlers.shift();
if (this.#state === FUIFILLED) {
if (typeof onFulfilled === "function") {
onFulfilled(this.#result);
} else {
resolve(this.#result);
}
} else {
if (typeof onRejected === "function") {
onRejected(this.#result);
} else {
reject(this.#result);
}
}
}
}
当传入的回调是一个函数时
/**
* 执行handlers
* @returns {void} void
*/
#run() {
if (this.#state === PENDING) return;
// 循环遍历
while (this.#handlers.length) {
const { onFulfilled, onRejected, resolve, reject } = this.#handlers.shift();
if (this.#state === FUIFILLED) {
if (typeof onFulfilled === "function") {
const data = onFulfilled(this.#result);
resolve(data);
} else {
resolve(this.#result);
}
} else {
if (typeof onRejected === "function") {
const data = onRejected(this.#result);
resolve(data);
} else {
reject(this.#result);
}
}
}
}
当回调函数的返回结果是一个Promise
这里要具有promise互操作性
/**
* 执行handlers
* @returns {void} void
*/
#run() {
if (this.#state === PENDING) return;
// 循环遍历
while (this.#handlers.length) {
const { onFulfilled, onRejected, resolve, reject } = this.#handlers.shift();
if (this.#state === FUIFILLED) {
if (typeof onFulfilled === "function") {
try {
const data = onFulfilled(this.#result);
if (this.#isPromiseLike(data)) {
data.then(resolve, reject);
} else {
resolve(data);
}
} catch (error) {
reject(error);
}
} else {
resolve(this.#result);
}
} else {
if (typeof onRejected === "function") {
try {
const data = onRejected(this.#result);
if (this.#isPromiseLike(data)) {
data.then(resolve, reject);
} else {
resolve(data);
}
} catch (error) {
reject(error);
}
} else {
reject(this.#result);
}
}
}
}
写到这里可以看到有很多重复代码,提取出公共部分,精炼如下:
/**
* 是否是一个promise
* @param {any} value 参数
* @returns {boolean} boolean
*/
#isPromiseLike(value) {
return false;
}
/**
* 微任务
* @param {Function} func 回调函数
* @returns {void} void
*/
#runMicrotask(func) {
setTimeout(func, 0);
}
/**
* 抽取公共函数,遵循DRY原则
* @param {Function} callback onFulfilled&onRejected回调函数
* @param {Function} resolve 成功回调
* @param {Function} reject 失败回调
* @returns {void} void
*/
#runOne(callback, resolve, reject) {
this.#runMicrotask(() => {
if (typeof callback !== "function") {
const settled = this.#state === FUIFILLED ? resolve : reject;
settled(this.#result);
return;
}
try {
const data = callback(this.#result);
if (this.#isPromiseLike(data)) {
data.then(resolve, reject);
} else {
resolve(data);
}
} catch (error) {
reject(error);
}
});
}
/**
* 执行handlers
* @returns {void} void
*/
#run() {
if (this.#state === PENDING) return;
// 循环遍历
while (this.#handlers.length) {
const { onFulfilled, onRejected, resolve, reject } = this.#handlers.shift();
if (this.#state === FUIFILLED) {
this.#runOne(onFulfilled, resolve, reject);
} else {
this.#runOne(onRejected, resolve, reject);
}
}
}
- #runOne函数抽取了#run函数里的公共部分遵循DRY(Don't Repeat Yourself)原则,放在一个微任务队列里
- #runMicrotask函数是模拟一个微任务
- #isPromiseLike函数作用是判断一个值是否是一个promise
#isPromiseLike判断一个值是否是一个promise
就是判断该值是否带有一个then方法
/**
* 是否是一个promise
* @param {any} value 参数
* @returns {boolean} boolean
*/
#isPromiseLike(value) {
if (value !== null && (typeof value === "object" || typeof value === "function")) {
return typeof value.then === "function";
}
return false;
}
#runMicrotask模拟一个微任务队列
区分环境:Node和Window
/**
* 微任务
* @param {Function} func 回调函数
* @returns {void} void
*/
#runMicrotask(func) {
// 区分环境Node和Window
if (typeof process === "object" && typeof process.nextTick === "function") {
process.nextTick(func);
} else if (typeof MutationObserver === "function") {
const ob = new MutationObserver();
const textNode = document.createTextNode("1");
ob.observe(textNode, { characterData: true });
textNode.data = "2";
} else {
setTimeout(func, 0);
}
}
至此,一个完整的PromiseA+规范代码就完成了,ES6中的Promise还有很多实例方法和静态方法,都是在这个then方法基础上搭建出来的,后面再进一步完善一部分方法,完整的代码链接地址gitee
3、补充其他实例方法和静态方法
上述的MyPromise是PromiseA+规范的代码,而ES中的Promise除了构造函数和then方法外,还有其他的方法诸如实例方法catch、finally,静态方法Promise.resolve、Promise.reject、Promise.all、Promise.race和Promise.allSettled等等,下面就一一写出这些函数的代码
Promise.prototype.catch
catch实例方法其实就是then(null, onRejected)
的简写,MDN文档也这么介绍的
/**
* catch实例方法
* @param {Function} onRejected 拒绝回调
* @returns {MyPromise} 新的MyPromise
*/
catch(onRejected) {
return this.then(undefined, onRejected);
}
Promise.prototype.finally
finally实例方法与then(onFinally,onFinally)
类似,但是有两个不同的地方
第一个是finally(onFinally)中onFinally函数不带有任何参数
第二个是返回的Promise的状态是依据上一个Promise的状态,具有穿透性
/**
* finally实例方法
* @param {Function} onFinally 回调函数
* @returns {MyPromise} 新的MyPromise
*/
finally(onFinally) {
return this.then(
data => {
onFinally();
return data;
},
err => {
onFinally();
throw err;
}
);
}
resolve静态方法
这里的resolve方法其实简单就三种情况:
- 参数是一个Promise,那么返回这个Promise
- 参数是一个thenable对象,那么调用其then方法
- 参数除上述两种之外,直接resolve
/**
* resolve静态方法
* @param {any} value 参数值
* @returns {MyPromise} 新的MyPromise
*/
static resolve(value) {
if (value instanceof MyPromise) return value;
let _resolve, _reject;
const p = new MyPromise((resolve, reject) => {
_resolve = resolve;
_reject = reject;
});
if (p.#isPromiseLike(value)) {
value.then(_resolve, _reject);
} else {
_resolve(value);
}
return p;
}
这里注意:静态方法里是不能使用this,否则会指向全局,所以先new一个Promise,再返回这个Promise
reject静态方法
根据MDN官方文档描述,reject就是返回一个带有拒绝原因的Promise
/**
* reject静态方法
* @param {Function} onRejected 拒绝回调
* @returns {MyPromise} 新的MyPromise
*/
static reject(onRejected) {
return new MyPromise((resolve, reject) => {
reject(onRejected);
});
}
all静态方法
all方法接受一个promises可迭代对象作为参数,返回一个新的Promise,其状态与可迭代对象的每一个成员有关,只要全部成员成功了就fulfilled,而有一个失败了就rejected
/**
* 判断是否是迭代器
* @param {any} value 参数
* @returns {boolean} boolean
*/
#isIterator(value) {
if (typeof value === "string") {
return true;
}
if (value !== null && (typeof value === "object" || typeof value === "function")) {
return typeof value[Symbol.iterator] === "function";
}
return false;
}
/**
* 静态方法all
* @param {Iterable} proms 参数
* @returns {MyPromise} 新的MyPromise
*/
static all(proms) {
let _resolve, _reject;
let count = 0,
finishNum = 0;
const result = [];
const p = new MyPromise((resolve, reject) => {
_resolve = resolve;
_reject = reject;
});
if (p.#isIterator(proms)) {
for (const prom of proms) {
const index = count; // 获取下标
count++;
MyPromise.resolve(prom).then(data => {
result[index] = data; // 将成功的值加入数组中
finishNum++;
if (finishNum === count) {
// 全部完成结束
_resolve(result);
}
}, _reject);
}
// 表示空数组
if (count === 0) {
_resolve([]);
}
} else {
// 如果iterator不是可迭代对象,则reject
_reject(`TypeError: ${typeof proms} ${proms} is not iterable (cannot read property Symbol(Symbol.iterator))`);
}
return p;
}
这里注意判断一个值是否是可迭代对象时,字符串是的,其他的对象带有[Symbol.Iterator]属性且是函数的即是
race静态方法
与all不同的是,返回的Promise的状态取决第一个成员状态的结束,无论成功或者失败
/**
* 静态方法race
* @param {Iterable} proms 可迭代对象
* @returns 新的MyPromise
*/
static race(proms) {
let _resolve, _reject;
const p = new MyPromise((resolve, reject) => {
_resolve = resolve;
_reject = reject;
});
// 谁先结束状态就发生变化
if (p.#isIterable(proms)) {
for (const prom of proms) {
MyPromise.resolve(prom).then(_resolve, _reject);
}
} else {
// 如果iterator不是可迭代对象,则reject
_reject(`TypeError: ${typeof proms} ${proms} is not iterable (cannot read property Symbol(Symbol.iterator))`);
}
return p;
}
allSettled静态方法
这里根据MDN介绍,输入的所有 promise 都已敲定时(包括传递空的可迭代类型),返回的 promise 将兑现,并带有描述每个 promsie 结果的对象数组。
/**
* 静态方法allSettled
* @param {*} proms 可迭代对象
* @returns 新的MyPromise
*/
static allSettled(proms) {
let _resolve, _reject;
let count = 0,
total = 0;
const result = [];
const p = new MyPromise((resolve, reject) => {
_resolve = resolve;
_reject = reject;
});
if (p.#isIterable(proms)) {
for (const prom of proms) {
const index = count;
count++;
MyPromise.resolve(prom)
.then(value => {
result[index] = {
status: "fulfilled",
value
};
})
.catch(reason => {
result[index] = {
status: "rejected",
reason
};
})
.finally(() => {
total++;
if (total === count) {
// 全部状态都结束
_resolve(result);
}
});
}
// 如果可迭代对象是空的则返回空数组
if (count === 0) {
_resolve([]);
}
} else {
// 如果iterator不是可迭代对象,则reject
_reject(`TypeError: ${typeof proms} ${proms} is not iterable (cannot read property Symbol(Symbol.iterator))`);
}
return p;
}
完结撒花~~~