从零到一实现完全符合Promise/A+
的Promise
- 本文源代码
- 参考文章:Promise的源码实现(完美符合Promise/A+规范)
- 笔者翻译的Promise/A+规范
Promise/A+
规范专注于提供一个通用的then
方法,对于如何创建、解决以及拒绝promise
并没有进行处理,对于Promise.all
,Promise.catch
等方法也没有进行明文规定。所以我们这里的实现也是以一个通用的then
方法为核心
为了方便理解:
- 文中
.then
中传入的俩个回调函数我们分别用onFulfilled
和onRejected
来代指。 - 源码中的编号注释对应规范中的相应编号
基础使用场景
下面是一个Promise
最基础的使用方法:
const p = new Promise((resolve, reject) => {
resolve(100);
});
p.then((result) => {
console.log('result', result);
});
我们先让我们自己的Promise
满足上述用法。
由于我们对Promise
使用了new
关键字,所以Promise
是JavaScript
中的类。在执行Promise
的时候,传入了一个立即执行函数,函数的参数为resolve,reject
俩个回调函数。这样,我们得到如下实现:
class Promise {
constructor (executor) {
const resolve = (result) => {
};
const reject = (reason) => {
};
executor(resolve, reject);
}
}
Promise
有三种状态:pending
等待态,rejected
解决态,rejected
拒绝态。
Promise
的立即执行函数中,如果resolve
函数执行,Promise
的状态将会从pending
变为rejected
,并且Promise
的value
是resolve
执行时的参数。
reject
函数执行时和resolve
执行的逻辑类似,Promise
的状态将会从pending
变为rejected
,并且Promise
的reason
是reject
执行时的参数。
需要注意的是,Promise
的状态一旦从pending
变为其它状态,就不能再进行更改。到现在,我们可以得到如下代码:
// 2.1.
const PENDING = 'pending';
const RESOLVED = 'resolved';
const REJECTED = 'rejected';
class Promise {
constructor (executor) {
this.state = PENDING;
this.value = undefined; // 1.3. 任何合法的JavaScript值
this.reason = undefined; // 1.5. 表示为什么promise被拒绝的一个值
const resolve = (result) => {
if (this.state !== PENDING) return;
this.state = RESOLVED;
this.value = result;
};
const reject = (reason) => {
if (this.state !== PENDING) return;
this.state = REJECTED;
this.reason = reason;
};
executor(resolve, reject);
}
}
module.exports = Promise;
Promise
的实例上有一个.then
方法,它接收俩个回调函数作为参数。当Promise
的状态为resolved
时,用Promise
的value
作为参数,执行.then
中的第一个回调函数。当Promise
的状态为rejected
时,用Promise
的reason
作为参数执行第二个回调函数。代码如下:
class Promise {
// ...
then (onFulfilled, onRejected) {
if (this.state === RESOLVED) {
onFulfilled(this.value);
} else if (this.state === REJECTED) {
onRejected(this.reason);
}
}
}
到这里,需求已经实现了,但是代码完全是同步的,我们需要用setTimeout(()=> {...},0)
来实现异步,确保.then
中的回调函数是异步执行的。
class Promise {
// ...
then (onFulfilled, onRejected) {
if (this.state === RESOLVED) {
setTimeout(() => {
onFulfilled(this.value);
}, 0);
} else if (this.state === REJECTED) {
setTimeout(() &#