本文将从Promise使用方法,实现原理出发,来实现手写MyPromise。
Promise的三个状态
- 创建时是Pending
- resolve进入调用栈执行后是Fullfilled
- reject进入调用栈执行后是Rejected
1. 定义三种状态
const STATE = {
PENDING: "pending",
FULLFILLED: "fullfilled",
REJECTED: "rejected",
};
如何创建MyPromise对象?
const p = new MyPromise(cb);
为了创建MyPromise对象,我们需要定义MyPromise类
2. 定义MyPromise类
class MyPromise {
#state = STATE.PENDING;
constructor(cb) {
try {
cb();
} catch (err) {}
}
}
需要向MyPromise对象内传入回调函数
const p = new MyPromise((resolve, reject) => {
resolve("hi");
});
3. 处理创建Promise传入的回调函数
class MyPromise {
#state = STATE.PENDING;
#value;
constructor(cb) {
try {
cb(this.#onResolve, this.#onReject);
} catch (err) {
this.#onReject();
}
}
#onResolve = (value) => {
this.#state = STATE.FULLFILLED;
this.#value = value;
};
#onReject = (value) => {
this.#state = STATE.REJECTED;
this.#value = value;
};
}
由于回调函数只能resolve一次或者reject一次,所以需要添加一些处理逻辑
#onResolve = (value) => {
if (this.#state !== STATE.PENDING) return;
this.#state = STATE.FULLFILLED;
this.#value = value;
};
#onReject = (value) => {
if (this.#state !== STATE.PENDING) return;
this.#state = STATE.REJECTED;
this.#value = value;
};
由于Promise的resolve无论如何都是异步执行的,也就是说需要等待同步代码执行完毕后才会执行resolve,所以我们需要把执行resolve的功能放入微任务队列。
#onResolve = (value) => {
queueMicrotask(() => {
if (this.#state !== STATE.PENDING) return;
this.#state = STATE.FULLFILLED;
this.#value = value;
});
};
#onReject = (value) => {
queueMicrotask(() => {
if (this.#state !== STATE.PENDING) return;
this.#state = STATE.REJECTED;
this.#value = value;
});
};
使用then来消耗MyPromise
p.then((value) => {
console.log(value);
});
根据MDN的定义,then可传入两个callback,一个处理resolve,另一个处理reject
4. 实现then功能
then(thenCb, catchCb) {
if (thenCb != null) thenCb(this.#value);
if (catchCb != null) catchCb(this.#value);
}
但是这种方式有一个致命缺陷,还记得上文提到的resolve是异步吗,promise对象会在所有同步代码执行完毕后才会resolve,也就是说,才会把value赋值给this.#value。
又因为then这时是同步的,会先于resolve执行,所以这时执行thenCb(this.#value)传入的参数是undefined
所以我们需要想办法等待resolve执行完毕后再执行thenCb
解决方法:使用Array存入thenCb和catchCb
#thenCallbacks = [];
#catchCallbacks = [];
#runCallbacks = () => {
if (this.#state === STATE.FULLFILLED) {
this.#thenCallbacks.forEach((thenCb) => {
thenCb(this.#value);
});
}
if (this.#state === STATE.REJECTED) {
this.#catchCallbacks.forEach((catchCb) => {
catchCb(this.#value);
});
}
};
then(thenCb, catchCb) {
if (thenCb != null) {
this.#thenCallbacks.push(thenCb);
}
if (catchCb != null) {
this.#catchCallbacks.push(catchCb);
}
this.#runCallbacks(); //有时候resolve执行后又执行then,就不能等待resolve了
}
调用栈中的同步代码执行完毕后,resolve将thenCallbacks中的thenCb全部抽出来执行
#onResolve = (value) => {
queueMicrotask(() => {
if (this.#state !== STATE.PENDING) return;
this.#state = STATE.FULLFILLED;
this.#value = value;
this.#runCallbacks();
});
};
#onReject = (value) => {
queueMicrotask(() => {
if (this.#state !== STATE.PENDING) return;
this.#state = STATE.REJECTED;
this.#value = value;
this.#runCallbacks();
});
};
5、实现then链式调用
根据Promise定义,then需要返回一个新的Promise
/**
*
* @param {Function} thenCb
* @param {Function} catchCb
* @returns {MyPromise}
*/
then(thenCb, catchCb) {
return new MyPromise((resolve, reject) => {
this.#thenCallbacks.push((result) => {
resolve(thenCb(result));
});
this.#catchCallbacks.push((reason) => {
reject(catchCb(reason));
});
this.#runCallbacks();
});
}
这时候我们已经可以实现一个简单的链式调用.
const p = new MyPromise((resolve, reject) => {
resolve("hi");
});
p.then((value) => {
console.log(value); // hi
return "ethan";
}).then((value) => {
console.log(value); // ethan
});
以及reject捕获
const p = new MyPromise((resolve, reject) => {
reject("error");
});
p.then(
(value) => {
console.log(value);
return "ethan";
},
(err) => {
console.log(err); // error
return "new promise";
}
).then((value) => {
console.log(value); // new promise
});
6.实现catch
catch的实现非常简单
/**
*
* @param {Function} catchCb
* @returns {MyPromise}
*/
catch(catchCb) {
return this.then(undefined, catchCb);
}
7. 实现finally
finally的定义是:无论resolve还是reject,都执行传入的callback,并且返回一个新的Promise,resolve的是传入finally的value
/**
* 无论resolve还是reject,都执行传入的callback,并将value传下去
* @param {Function} cb
* @returns {MyPromise}
*/
finally(cb) {
return this.then(
(result) => {
cb();
return result;
},
(reason) => {
cb();
return reason;
}
);
}
8.实现catch的链式调用
目前catch有一个问题
const p = new MyPromise((resolve, reject) => {
resolve("hi");
});
p.catch((reason) => {
console.log(reason);
return "ethan";
}).then((value) => {
console.log(value);
});
此时的then接不住p的hi,所以我们需要在resolve的情况下忽略catch。
办法就是在then中添加逻辑,当thenCb为空时,就直接把then创建的MyPromise resolve掉,不需要调用catchCb了。
then(thenCb, catchCb) {
return new MyPromise((resolve, reject) => {
const thenCallback = (result) => {
//*****添加的代码
if (thenCb == null) {
resolve(result);
return;
}
//****************
try {
resolve(thenCb(result));
} catch (err) {
reject(err);
}
};
this.#thenCallbacks.push(thenCallback);
const catchCallback = (reason) => {
try {
resolve(catchCb(reason));
} catch (err) {
reject(err);
}
};
this.#catchCallbacks.push(catchCallback);
this.#runCallbacks();
});
}
同理,当出现下述情况时,也需要在then中添加逻辑
const p = new MyPromise((resolve, reject) => {
reject("hi");
});
p.then((reason) => {
console.log(reason);
return "ethan";
}).catch((value) => {
console.log(value);
});
我们需要忽略掉then,将rejected的状态以及reason传下去
then(thenCb, catchCb) {
return new MyPromise((resolve, reject) => {
const thenCallback = (result) => {
if (thenCb == null) {
resolve(result);
return;
}
try {
resolve(thenCb(result));
} catch (err) {
reject(err);
}
};
this.#thenCallbacks.push(thenCallback);
const catchCallback = (reason) => {
//*****添加的代码
if (catchCb == null) {
reject(reason);
return;
}
//*****添加的代码
try {
resolve(catchCb(reason));
} catch (err) {
reject(err);
}
};
this.#catchCallbacks.push(catchCallback);
this.#runCallbacks();
});
}
9.处理then返回Promise的情况
当then返回Promise时,会出现错误
const p = new MyPromise((resolve, reject) => {
resolve("hi");
});
p.then((value) => {
return new MyPromise((resolve, reject) => {
resolve("ethan");
});
}).then((value) => {
console.log(value);
//MyPromise {#state: 'fullfilled', #value: 'ethan', …}
});
此时我们需要先消耗then中产生的MyPromise,然后传递给下方
/**
*
* @param {MyPromise OR Object} value
*/
#onResolve = (value) => {
queueMicrotask(() => {
if (this.#state !== STATE.PENDING) return;
//*****添加的代码
if (value instanceof MyPromise) {
value.then(this.#onResolve, this.#onReject);
return;
}
//*****添加的代码
this.#state = STATE.FULLFILLED;
this.#value = value;
this.#runCallbacks();
});
};
这时console.log就正常了
const p = new MyPromise((resolve, reject) => {
resolve("hi");
});
p.then((value) => {
return new MyPromise((resolve, reject) => {
resolve("ethan");
});
}).then((value) => {
console.log(value); // ethan
});
10. 实现静态方法Promise.resolve(),Promise.reject()
static resolve = (value) => {
return new MyPromise((resolve, reject) => {
resolve(value);
});
};
static reject = (reason) => {
return new MyPromise((resolve, reject) => {
reject(reason);
});
};
测试
const p = MyPromise.resolve("hi");
p.then((value) => {
console.log(value); // hi
});
11. 实现静态方法Promise.all()
Promse.all()的定义是等待所有resolve返回array,只要有一个reject就reject。
/**
* 等待所有resolve返回array,只要有一个reject就reject
* @param {Array} promises
* @returns {MyPromise}
*/
static all = (promises) => {
const results = [];
let successPromises = 0;
return new MyPromise((resolve, reject) => {
promises.forEach((promise) => {
promise
.then((value) => {
successPromises++;
results.push(value);
if (successPromises === promises.length) {
resolve(results);
}
})
.catch((err) => {
reject(err);
});
});
});
};
测试
const p1 = new MyPromise((resolve, reject) => {
resolve("hi");
});
const p2 = new MyPromise((resolve, reject) => {
resolve("ethan");
});
Promise.all([p1, p2]).then((results) => {
console.log(results); // ['hi', 'ethan']
});
const p3 = new MyPromise((resolve, reject) => {
reject("error");
});
Promise.all([p1, p2, p3])
.then((results) => {
console.log(results);
})
.catch((err) => {
console.log(err); // error
});
12. 实现Promise.allSettled()
/**
* 无论resolve还是reject,都放进结果集中,结果集中的是MyPromise,而不是value
* @param {Array} promises
* @returns MyPromise
*/
static allSettled = (promises) => {
const results = [];
return new MyPromise((resolve) => {
promises.forEach((promise) => {
promise
.then(() => {
results.push(promise);
})
.catch(() => {
results.push(promise);
});
});
resolve(results);
});
};
测试
const p1 = new MyPromise((resolve, reject) => {
resolve("hi");
});
const p2 = new MyPromise((resolve, reject) => {
resolve("ethan");
});
const p3 = new MyPromise((resolve, reject) => {
reject("error");
});
MyPromise.allSettled([p1, p2, p3])
.then((results) => {
console.log(results);
})
.catch((err) => {
console.log(err);
});
13. 实现静态方法Promise.race()
/**
* 返回第一个resolve或者reject的Promise的value
* @param {Array} promises
* @returns {MyPromise}
*/
static race = (promises) => {
return new MyPromise((resolve, reject) => {
promises.forEach((promise) => {
promise
.then((value) => resolve(value))
.catch((reason) => reject(reason));
});
});
};
测试
const p1 = new MyPromise((resolve, reject) => {
resolve("hi");
});
const p2 = new MyPromise((resolve, reject) => {
resolve("ethan");
});
const p3 = new MyPromise((resolve, reject) => {
reject("error");
});
MyPromise.race([p1, p2, p3])
.then((results) => {
console.log(results); // hi
})
.catch((err) => {
console.log(err);
});
14. 实现静态方法Promise.any()
/**
* 有一个resolve就返回resolve的value,否则返回error
* @param {Array} promises
* @returns {MyPromise}
*/
static any = (promises) => {
return new MyPromise((resolve, reject) => {
const results = [];
let rejectedPromises = 0;
promises.forEach((promise) => {
promise
.then((value) => {
resolve(value);
})
.catch((err) => {
rejectedPromises++;
results.push(err);
if (rejectedPromises === promises.length) {
reject("AggregateError: All promises were rejected");
}
});
});
});
};
测试
const p1 = new MyPromise((resolve, reject) => {
resolve("hi");
});
const p2 = new MyPromise((resolve, reject) => {
resolve("ethan");
});
const p3 = new MyPromise((resolve, reject) => {
reject("error");
});
MyPromise.any([p1, p2, p3])
.then((results) => {
console.log(results); // hi
})
.catch((err) => {
console.log(err);
});
MyPromise.any([p3, p3, p3])
.then((results) => {
console.log(results); // AggregateError: All promises were rejected
})
.catch((err) => {
console.log(err);
});