前言
Promise 的基本使用可以查看阮一峰老师的《ECMAScript 6 入门》 。
本文主要讲解手写 Promise 核心原理以及完整的 Promises/A+ 实现。
Promise 核心
Promise 对象代表一个异步操作,它有三种状态:pending(待定)、fulfilled(已成功)和 rejected(已拒绝)。而一个 Promise 执行完成状态只会存在以下情况之一:
- 待定
(pending): 初始状态,既没成功,也没拒绝。 - 已成功
(fulfilled): 执行成功。 - 已拒绝
(rejected): 执行失败。
Promise 一旦状态改变,就不会再变,任何时候都可以得到这个结果。Promise 对象的状态改变,只有两种可能:从 pending 变为 fulfilled 和从 pending 变为 rejected 。
Promise 就好比我们去汉堡店购买汉堡,当我们跟服务员说要购买汉堡时(执行 promise),此时状态为 pending 状态;稍许片刻后,服务员说汉堡卖完了,此时状态为 rejected ;或者服务员拿来了汉堡,此时状态为 fulfilled 。
我们来看下 Promise 例子:
const p1 = new Promise((resolve, reject) => {
resolve('成功')
reject('失败')
})
console.log('p1', p1)
const p2 = new Promise((resolve, reject) => {
reject('失败')
resolve('成功')
})
console.log('p2', p2)
const p3 = new Promise((resolve, reject) => {
throw('报错')
})
console.log('p3', p3)
const p4 = new Promise(() => {})
console.log('p4', p4)
const p5 = new Promise((resolve, reject) => {
const msg = 'hello world'
})
console.log('p5', p5)
p5.then(() => {
console.log('p5执行了then')
})
// 输出结果
p1 Promise {<fulfilled>: '成功'}
[[Prototype]]: Promise
[[PromiseState]]: "fulfilled"
[[PromiseResult]]: "成功"
p2 Promise {<rejected>: '失败'}
[[Prototype]]: Promise
[[PromiseState]]: "rejected"
[[PromiseResult]]: "失败"
p3 Promise {<rejected>: '失败'}
[[Prototype]]: Promise
[[PromiseState]]: "rejected"
[[PromiseResult]]: "失败"
p4 Promise {<pending>}
[[Prototype]]: Promise
[[PromiseState]]: "pending"
[[PromiseResult]]: undefined
p5 Promise {<pending>}
[[Prototype]]: Promise
[[PromiseState]]: "pending"
[[PromiseResult]]: undefined
Promise {<pending>}
[[Prototype]]: Promise
[[PromiseState]]: "pending"
[[PromiseResult]]: undefined
从上述例子中,我们可以得出以下知识点:
- 执行
resolve(),Promise状态将变成fulfilled,即 已成功状态 - 执行
reject(),Promise状态将变成rejected,即 已拒绝状态 Promise只以第一次为准,状态一旦改变就不会再变。第一次成功或拒绝,状态将永久变为fulfilled或rejectedPromise中存在throw语句,就相当于执行了reject()Promise的初始状态为pendingPromise中没有执行resolve、reject、throw,状态依旧为pendingPromise状态为pending时,将不会执行then回调函数
另外还需注意的是,当 Promise 对象什么都不传时,执行会报错:
const p6 = new Promise()
console.log('p6', p6)
// 输出结果 报错
Uncaught TypeError: Promise resolver undefined is not a function
at new Promise (<anonymous>)
at <anonymous>:1:18
这里包含一个知识点:
规定必须给 Promise 对象传入一个执行函数,否则将会报错
Promise 实现
定义初始结构
我们先来看下原生 Promise 的使用:
// 通过 new 创建 promise 实例
const promise = new Promise()
上述代码可以看出,Promise 对象通过 new 来创建一个 Promise 实例。所以我们可以使用 构造函数 或者 class 类来手写,这里我们选择使用 class 来进行创建:
class myPromise {}
上文我们提到 Promise 必须传入一个执行函数,否则将报错:
const promise = new Promise(() => {})
我们可以通过 class 的构造函数 constructor 来接收一个参数 executor (函数类型),并执行该参数:
class myPromise {
constructor(executor) {
executor();
}
}
实现 resolve 和 reject
原生 Promise 传入的函数中会传入 resolve 和 reject 两个参数:
const promise = new Promise((resolve, reject) => {})
那我们手写时也传入相同的两个参数:
class myPromise {
constructor(executor) {
executor(resolve, reject);
}
}
而我们知道,原生中 resolve 和 reject 都是以函数形式来执行的,所以我们还需定义两个函数,改写如下:
class myPromise {
constructor(executor) {
// 注意 class 中调用自身方法 需加上 this
executor(this.resolve, this.reject);
}
resolve() {}
reject() {}
}
状态管理
上文我们了解到 Promise 存在三个状态:pending(待定)、fulfilled(已成功)和 rejected(已拒绝),而 Promise 对象的状态改变,只有两种可能:
- 初始状态
pending pending转为fulfilledpending转为rejected
因此,我们也需要定义这三个状态:
class myPromise {
static PENDING = 'pending'; // 待定
static FULFILLED = 'fulfilled'; // 已成功
static REJECTED = 'rejected'; // 已拒绝
constructor(executor) {
executor(this.resolve, this.reject);
}
resolve() {}
reject() {}
}
另外我们知道,原生 Promise 通过 PromiseState 来保存实例的状态属性,所以我们也可以通过 this.PromiseState 来保存实例的状态属性,该属性默认为 pending 状态:
class myPromise {
static PENDING = 'pending';
static FULFILLED = 'fulfilled';
static REJECTED = 'rejected';
constructor(executor) {
this.PromiseState = myPromise.PENDING; // 默认 pending 状态
executor(this.resolve, this.reject);
}
resolve() {}
reject() {}
}
那么在执行 resolve() 函数时需判断状态是否为 pending(待定) ,如果是 pending(待定) 就把状态改为 fulfilled(成功) :
class myPromise {
// 省略
resolve() {
// 判断状态是否为 pending --> fulfilled
if (this.PromiseState === myPromise.PENDING) {
this.PromiseState = myPromise.FULFILLED;
}
}
reject() {}
}
同样,给 reject 也添加相应的判断逻辑:
class myPromise {
// 省略
reject() {
// 判断状态是否为 pending --> rejected
if (this.PromiseState === myPromise.PENDING) {
this.PromiseState = myPromise.REJECTED;
}
}
}
执行 resolve 和 reject 可传参
原生 Promise 在执行 resolve() 和 reject() 时可以传入一个参数:
const promise = new Promise((resolve, reject) => {
resolve('hello world')
})
我们可以把该结果参数命名为 PromiseResult (同原生 Promise 一致),定义初始值为 null,并把 resove 和 reject 执行结果赋予该值:
class myPromise {
static PENDING = 'pending';
static FULFILLED = 'fulfilled';
static REJECTED = 'rejected';
constructor(executor) {
this.PromiseState = myPromise.PENDING;
this.PromiseResult = null;
executor(this.resolve, this.reject);
}
resolve(result) {
if (this.PromiseState === myPromise.PENDING) {
this.PromiseState = myPromise.FULFILLED;
this.PromiseResult = result; // 成功结果
}
}
reject(reason) {
if (this.PromiseState === myPromise.PENDING) {
this.PromiseState = myPromise.REJECTED;
this.PromiseResult = reason; // 拒绝原因
}
}
}
代码写到这,我们来测下执行情况:
// 声明一个 myPromise 实例
const p1 = new myPromise((resolve, reject) => {
resolve('测试')
})
// 输出结果 报错
Uncaught TypeError: Cannot read properties of undefined (reading 'PromiseState')
at resolve (<anonymous>:12:18)
at <anonymous>:26:5
at new myPromise (<anonymous>:9:9)
at <anonymous>:25:20
可见测试结果报错了,从报错结果分析出 PromiseState 我们已经在 constructor 中创建,而不应该是 undefined。而执行 resolve 和 reject 方法时,PromiseState 是被 this 指向的,那说明调用 this.PromiseState 并没有调用 constructor 里的 this.PromiseState ,也就是说这里的 this 丢失了。
根据阮一峰老师的《ECMAScript 6 入门》 中 Class 的基本语法 里, this 的指向 一节讲到,如果将这个方法(这里指 resolve)提取出来单独使用,this 会指向该方法运行时所在的环境(由于 class 内部是严格模式,所以 this 实际指向的是 undefined),从而导致找不到 PromiseState 而报错。
解决方法我们可以使用 箭头函数 或者 bind ,这里我们使用 bind 来绑定 this :
class myPromise {
static PENDING = 'pending';
static FULFILLED = 'fulfilled';
static REJECTED = 'rejected';
constructor(executor) {
this.PromiseState = myPromise.PENDING;
this.PromiseResult = null;
// 使用 bind 修改 this 指向 原因是 myPromise 创建实例后 单独调用 resolve 会导致 this指向丢失
executor(this.resolve.bind(this), this.reject.bind(this));
}
resolve(result) {
// 省略
}
reject(reason) {
// 省略
}
}
改造后,我们再来测试看下:
const myPromise1 = new myPromise((resolve, reject) => {
resolve('成功')
})
console.log(myPromise1)
// 输出结果
myPromise {PromiseState: 'fulfilled', PromiseResult: '成功'}
PromiseResult: "成功"
PromiseState: "fulfilled"
[[Prototype]]: Object
const myPromise2 = new myPromise((resolve, reject) => {
reject('失败')
})
console.log(myPromise2)
// 输出结果
myPromise {PromiseState: 'rejected', PromiseResult: '失败'}
PromiseResult: "失败"
PromiseState: "rejected"
[[Prototype]]: Object
我们再使用原生 Promise 看下:
const promise1 = new Promise((resolve, reject) => {
resolve('成功')
})
console.log(promise1)
// 输出结果
Promise {<fulfilled>: '成功'}
[[Prototype]]: Promise
[[PromiseState]]: "fulfilled"
[[PromiseResult]]: "成功"
const promise2 = new Promise((resolve, reject) => {
reject('失败')
})
console.log(promise2)
// 输出结果
Promise {<rejected>: '失败'}
[[Prototype]]: Promise
[[PromiseState]]: "rejected"
[[PromiseResult]]: "失败"
执行结果符合我们预期,接下来再看下 then 方法的实现。
实现 then 方法
我们先来看下原生 Promise 是如何使用的:
const promise = new Promise((resolve, reject) => {
resolve('成功')
})
promise.then(
result => {
console.log(result)
},
reason => {
console.log(reason)
}
)
可以看出,then 方法是在创建完实例后再调用,并且可以传入两个参数(都是函数类型),一个状态为 fulfilled 时执行,另一个状态为 rejected 时执行,所以我们要添加以下逻辑:
- 增加一个
then方法 then方法传入两个参数onFulfilled和onRejected,分别代表成功和失败时的回调函数- 由于
Promise状态改变是不可逆的,只能从pending变为rejected或从pending变为rejected,所以还需加上状态判断
class myPromise {
static PENDING = 'pending';
static FULFILLED = 'fulfilled';
static REJECTED = 'rejected';
constructor(executor) {
this.PromiseState = myPromise.PENDING;
this.PromiseResult = null;
executor(this.resolve.bind(this), this.reject.bind(this));
}
resolve(result) {
if (this.PromiseState === myPromise.PENDING) {
this.PromiseState = myPromise.FULFILLED;
this.PromiseResult = result;
}
}
reject(reason) {
if (this.PromiseState === myPromise.PENDING) {
this.PromiseState = myPromise.REJECTED;
this.PromiseResult = reason;
}
}
// 改造后
then(onFulfilled, onRejected) {
// 状态为 fulfilled
if (this.PromiseState === myPromise.FULFILLED) {
// 执行成功后的回调
onFulfilled(this.PromiseResult);
}
// 状态为 rejected
if (this.PromiseState === myPromise.REJECTED) {
// 执行失败后的回调
onRejected(this.PromiseResult);
}
}
}
改造完后,我们再测试看下:
const myPromise1 = new myPromise((resolve, reject) => {
resolve('成功')
})
myPromise1.then(
result => {
console.log(result)
},
reason => {
console.log(reason)
}
)
// 输出结果
"成功"
const myPromise2 = new myPromise((resolve, reject) => {
reject('失败')
})
myPromise2.then(
result => {
console.log(result)
},
reason => {
console.log(reason)
}
)
// 输出结果
"失败"
执行结果达到预期,另外,原生 Promise 底层还处理了很多特殊情况,接下来我们再来聊下执行异常 throw 。
执行异常 throw
在实例化 Promise 后,执行函数如果抛出错误,那么在 then 回调函数的第二个参数 即 状态为 rejected 回调函数中输出错误信息。这里存在一个疑问,平时我们接收错误信息是在 catch 方法中接收,可为什么说是在 then 方法第二个参数中呢?
事实上,我们调用 Promise.catch() 是显示调用,其内部实际调用的是 .then(null, onRejected) 。Promise.prototype.catch() 方法是 .then(null, rejection) 或 .then(undefined, rejection) 的别名,用于指定发生错误时的回调函数。
Promise.then(
val => console.log('fulfilled:', val)
)
.catch(
error => console.log('rejected', error)
);
// 等同于
Promise.then(
null,
error => { console.log(error) }
)
// 例子
const promise = new Promise((resolve, reject) => {
throw new Error('test')
})
promise.catch(error => {
console.log(error)
})
// 输出结果
Error: test
上述案例等价于:
// 第一种
const promise = new Promise((resolve, reject) => {
reject(new Error('test'))
})
// 第二种
const promise = new Promise((resolve, reject) => {
try {
throw new Error('test')
} catch (error) {
reject(error)
}
})
promise.catch(error => {
console.log(error)
})
// 输出结果
Error: test
上述写法得出,reject 方法作用等同于抛出错误,因此我们就可以用 try/catch 来处理异常。另外,平时写法更推荐使用 catch 来捕获错误,而不推荐 then 方法第二个回调函数,原因是可以捕获前面 then 方法执行中的错误,也更接近同步的写法( try/catch )。
接下来,我们先按原来代码测试一下 throw :
class myPromise {
static PENDING = 'pending';
static FULFILLED = 'fulfilled';
static REJECTED = 'rejected';
constructor(executor) {
this.PromiseState = myPromise.PENDING;
this.PromiseResult = null;
executor(this.resolve.bind(this), this.reject.bind(this));
}
resolve(result) {
if (this.PromiseState === myPromise.PENDING) {
this.PromiseState = myPromise.FULFILLED;
this.PromiseResult = result;
}
}
reject(reason) {
if (this.PromiseState === myPromise.PENDING) {
this.PromiseState = myPromise.REJECTED;
this.PromiseResult = reason;
}
}
then(onFulfilled, onRejected) {
if (this.PromiseState === myPromise.FULFILLED) {
onFulfilled(this.PromiseResult);
}
if (this.PromiseState === myPromise.REJECTED) {
onRejected(this.PromiseResult);
}
}
}
const myPromise1 = new myPromise((resolve, reject) => {
throw new Error('失败')
})
myPromise1.then(
result => {
console.log('fulfilled:', result)
},
reason => {
console.log('rejected:', reason)
}
)
// 输出结果
Uncaught Error: 失败
可以看出执行报错了,并没有捕获到错误信息,也没输出错误内容。所以我们可添加以下逻辑:
- 在执行
resolve()和reject()之前用try/catch进行判断 - 如果没有报错,就正常执行
resolve()和reject()方法 - 否则就把错误信息传入
reject()方法,并直接执行reject()
class myPromise {
static PENDING = 'pending';
static FULFILLED = 'fulfilled';
static REJECTED = 'rejected';
constructor(executor) {
this.PromiseState = myPromise.PENDING;
this.PromiseResult = null;
// 执行 resolve 和 reject 之前使用 try/catch , 如果报错 通过 catch 来捕获错误
try {
executor(this.resolve.bind(this), this.reject.bind(this));
} catch (error) {
this.reject(error);
}
}
resolve(result) {
if (this.PromiseState === myPromise.PENDING) {
this.PromiseState = myPromise.FULFILLED;
this.PromiseResult = result;
}
}
reject(reason) {
if (this.PromiseState === myPromise.PENDING) {
this.PromiseState = myPromise.REJECTED;
this.PromiseResult = reason;
}
}
then(onFulfilled, onRejected) {
if (this.PromiseState === myPromise.FULFILLED) {
onFulfilled(this.PromiseResult);
}
if (this.PromiseState === myPromise.REJECTED) {
onRejected(this.PromiseResult);
}
}
}
改造后,我们再测试一下:
const myPromise1 = new myPromise((resolve, reject) => {
throw new Error('失败')
})
myPromise1.then(
result => {
console.log('fulfilled:', result)
},
reason => {
console.log('rejected:', reason)
}
)
// 输出结果
rejected: Error: 失败
可以看出,执行结果没有报错,并成功捕获了错误信息并输出。还需注意的是,catch 方法中不需要给 reject() 方法进行 this 绑定,因为这里是直接执行,而不是创建实例后再执行。对于构造函数constructor 中要执行的 executor() 方法里的 reject 只是作为参数传入,并不执行,只有创建实例后调用 reject() 方法时才执行,此时 this 的指向已改变,所以想要正确调用 myPromise 的 reject() 方法就要通过 bind 改变 this 指向。
参数校验
原生 Promise 规定 then 方法中的两个参数如果不是 函数 则直接忽略,我们来看下下面的代码:
const promise = new Promise((resolve, reject) => {
resolve('测试')
})
promise.then(
undefined,
reason => {
console.log('rejected:', reason)
}
)
// 输出结果
Promise {<fulfilled>: '测试'}
[[Prototype]]: Promise
[[PromiseState]]: "fulfilled"
[[PromiseResult]]: "测试"
可以看出,执行并没有报错。我们再执行下手写 myPromise 的代码:
class myPromise {
// 省略
}
const promise1 = new myPromise((resolve, reject) => {
resolve('测试')
})
promise1.then(
undefined,
reason => {
console.log('rejected:', reason)
}
)
// 输出结果
Uncaught TypeError: onFulfilled is not a function
结果 Uncaught TypeError: onFulfilled is not a function 报错了,这并不是我们想要的。我们再来看下之前手写 then 部分代码:
then(onFulfilled, onRejected) {
if (this.PromiseState === myPromise.FULFILLED) {
onFulfilled(this.PromiseResult);
}
if (this.PromiseState === myPromise.REJECTED) {
onRejected(this.PromiseResult);
}
}
上述代码逻辑分别执行成功或失败后的回调方法,但是我们不想去修改原逻辑,那么只能把参数类型不是为函数时修改为函数。
原生 Promise 规范如果 onFulfilled 和 onRejected 不是函数,则直接忽略。所谓忽略,并不是什么都不做。对于 onFulfilled 来说 就是将 value 原封不动的返回,对于 onRejected 来说就是返回 reason,onRejected 因为是错误分支,所以我们返回 reason 应该 throw 抛出一个错误。
改造如下:
then(onFulfilled, onRejected) {
// onFulfilled 如果为函数类型则直接赋值该函数 否则将 value 原封不动返回
onFulfilled = typeof onFulfilled === 'function' ? onFulfilled : value => value;
// onRejected 如果为函数类型则直接赋值该函数 否则将 throw 抛出错误
onRejected = typeof onRejected === 'function' ? onRejected : reason => { throw reason; };
if (this.PromiseState === myPromise.FULFILLED) {
onFulfilled(this.PromiseResult);
}
if (this.PromiseState === myPromise.REJECTED) {
onRejected(this.PromiseResult);
}
}
改造完后,我们再来测试一下:
class myPromise {
// 省略
}
const promise1 = new myPromise((resolve, reject) => {
resolve('测试')
})
promise1.then(
undefined,
reason => {
console.log('rejected:', reason)
}
)
console.log(promise1)
// 输出结果
myPromise {PromiseState: 'fulfilled', PromiseResult: '测试'}
PromiseResult: "测试"
PromiseState: "fulfilled"
[[Prototype]]: Object
结果没有报错,达到预期。另外还需说明下,这里成功后的返回值,需要在下次 then 成功回调中获取到,这里留个悬念,下文 then 方法链式调用中会讲解到。
当前实现的完整代码:
class myPromise {
static PENDING = 'pending';
static FULFILLED = 'fulfilled';
static REJECTED = 'rejected';
constructor(executor) {
this.PromiseState = myPromise.PENDING;
this.PromiseResult = null;
try {
executor(this.resolve.bind(this), this.reject.bind(this));
} catch (error) {
this.reject(error);
}
}
resolve(result) {
if (this.PromiseState === myPromise.PENDING) {
this.PromiseState = myPromise.FULFILLED;
this.PromiseResult = result;
}
}
reject(reason) {
if (this.PromiseState === myPromise.PENDING) {
this.PromiseState = myPromise.REJECTED;
this.PromiseResult = reason;
}
}
then(onFulfilled, onRejected) {
onFulfilled = typeof onFulfilled === 'function' ? onFulfilled : value => value;
onRejected = typeof onRejected === 'function' ? onRejected : reason => { throw reason; };
if (this.PromiseState === myPromise.FULFILLED) {
onFulfilled(this.PromiseResult);
}
if (this.PromiseState === myPromise.REJECTED) {
onRejected(this.PromiseResult);
}
}
}
实现异步
我们先来看下原生 Promise 异步的使用:
console.log(1);
const promise = new Promise((resolve, reject) => {
console.log(2);
resolve('成功');
})
promise.then(
result => {
console.log('fulfilled:', result);
},
reason => {
console.log('rejected:', reason)
}
)
console.log(3);
// 输出结果
1
2
3
fulfilled: 成功
来看下执行顺序:
- 首先执行
console.log(1)输出结果1 - 接着创建
Promise实例,并输出2,这里依旧是同步 - 然后执行
resolve修改结果值 - 接着执行
promise.then会进行异步操作,于是执行console.log(3)输出结果3 - 等执行栈中都清空后,再执行
promise.then里的内容,最后输出fulfilled: 成功
我们用同样的代码在手写 myPromise 上测试:
class myPromise {
// 省略
}
console.log(1);
const p1 = new myPromise((resolve, reject) => {
console.log(2);
resolve('成功');
})
p1.then(
result => {
console.log('fulfilled:', result);
},
reason => {
console.log('rejected:', reason)
}
)
console.log(3);
// 输出结果
1
2
fulfilled: 成功
3
可以看出只有 fulfilled: 成功 和 3 顺序不对,其实问题很简单,就是我们刚说的没有设置异步执行。
我们再来看下 Promises/A+ 规范是怎么定义的:
规范 2.2.4 :
onFulfilledoronRejectedmust not be called until the execution context stack contains only platform code.[3.1].
译文:
onFulfilled 和 onRejected 只有在 执行环境 堆栈仅包含平台代码时才可被调用 注1
规范对 2.2.4 做了注释:
3.1 Here “platform code” means engine, environment, and promise implementation code. In practice, this requirement ensures that
onFulfilledandonRejectedexecute asynchronously, after the event loop turn in whichthenis called, and with a fresh stack. This can be implemented with either a “macro-task” mechanism such assetTimeoutorsetImmediate, or with a “micro-task” mechanism such asMutationObserverorprocess.nextTick. Since the promise implementation is considered platform code, it may itself contain a task-scheduling queue or “trampoline” in which the handlers are called.
译文:
3.1 这里的平台代码指的是引擎、环境以及 promise 的实施代码。实践中要确保 onFulfilled 和 onRejected 方法异步执行,且应该在 then 方法被调用的那一轮事件循环之后的新执行栈中执行。这个事件队列可以采用“宏任务(macro-task)”机制,比如setTimeout 或者 setImmediate; 也可以采用“微任务(micro-task)”机制来实现, 比如 MutationObserver 或者process.nextTick。
由于 promise 的实施代码本身就是平台代码(译者注:即都是 JavaScript),故代码自身在处理程序时可能已经包含一个任务调度队列或『跳板』)。
接下来,我们使用规范中讲到的宏任务 setTimeout 来进行改造:
class myPromise {
// 省略
then(onFulfilled, onRejected) {
onFulfilled = typeof onFulfilled === 'function' ? onFulfilled : value => value;
onRejected = typeof onRejected === 'function' ? onRejected : reason => { throw reason; };
if (this.PromiseState === myPromise.FULFILLED) {
// 使用 setTimeout 设置异步 也可以使用 queueMicrotask
setTimeout(() => {
onFulfilled(this.PromiseResult);
})
}
if (this.PromiseState === myPromise.REJECTED) {
// 使用 setTimeout 设置异步 也可以使用 queueMicrotask
setTimeout(() => {
onRejected(this.PromiseResult);
})
}
}
}
// 执行
console.log(1);
const p1 = new myPromise((resolve, reject) => {
console.log(2);
resolve('成功');
})
p1.then(
result => {
console.log('fulfilled:', result);
},
reason => {
console.log('rejected:', reason)
}
)
console.log(3);
// 输出结果
1
2
3
fulfilled: 成功
可见,执行结果达到预期。另外还有种情况,我们在原生 Promise 中添加 setTimeout,让 resolve 也异步执行,那么就会存在这样一个问题,resolve 和 then 都是异步的,究竟谁会先执行呢?我们来看下以下案例:
console.log(1);
const promise = new Promise((resolve, reject) => {
console.log(2);
setTimeout(() => {
resolve('成功');
console.log(4);
});
})
promise.then(
result => {
console.log('fulfilled:', result);
},
reason => {
console.log('rejected:', reason)
}
)
console.log(3);
// 输出结果
1
2
3
4
fulfilled: 成功
这里涉及到浏览器的事件循环,而异步任务又分为:微任务(micro task)和 宏任务(macro task) ,这里不展开讲解,具体的可以查阅相关资料。我们只需要记住当 当前执行栈执行完毕时会立刻先处理所有微任务队列中的事件,然后再去宏任务队列中取出一个事件。同一次事件循环中,微任务永远在宏任务之前执行。
我们再来分析下上述代码执行顺序:
- 第一步,执行
console.log(1)输出结果 1。 - 第二步,创建
Promise实例,执行console.log(2)输出结果 2。 - 第三步,遇到
setTimeout塞入宏任务队列,等待执行。 - 第四步,执行
promise.then(),注册成功和失败后的回调,由于promise的状态目前处于pending 等待状态,所以并不会执行传入的两个回调。 - 第五步,执行
console.log(3)输出结果 3。 - 第六步,同步代码执行完毕,先检查微任务队列,微任务队列为空;再检查宏任务队列,宏任务队列存在需执行的任务,取第一个宏任务
setTimeout执行,执行时调用resolve,修改Promise状态为fulfilled 成功, 并将promise.then注册的回调推入微任务队列,接着执行console.log(4)输出结果 4。 - 第七步,同步代码执行完毕,再次执行上述事件循环,检查微任务队列,存在需要执行的微任务,按照
先进先出的顺序执行所有的微任务,此时输出结果 fulfilled: 成功。 - 执行完毕,输出结果:1、2、3、4、fulfilled: 成功。
回到正文,我们再用同样的代码执行手写 myPromise :
class myPromise {
// 省略
}
console.log(1);
const p1 = new myPromise((resolve, reject) => {
console.log(2);
setTimeout(() => {
resolve('成功');
console.log(4);
});
})
p1.then(
result => {
console.log('fulfilled:', result);
},
reason => {
console.log('rejected:', reason)
}
)
console.log(3);
// 输出结果
1
2
3
4
可以发现 fulfilled: 成功 并没有输出,我们可以设想下会不会 then 方法没有执行,先回顾下 then 方法的逻辑:
class myPromise {
// 省略
then(onFulfilled, onRejected) {
onFulfilled = typeof onFulfilled === 'function' ? onFulfilled : value => value;
onRejected = typeof onRejected === 'function' ? onRejected : reason => { throw reason; };
if (this.PromiseState === myPromise.FULFILLED) {
setTimeout(() => {
onFulfilled(this.PromiseResult);
})
}
if (this.PromiseState === myPromise.REJECTED) {
setTimeout(() => {
onRejected(this.PromiseResult);
})
}
}
}
可以看出很可能判断条件没满足,也就是状态不对。那么我们可以先在三个位置打印出状态,再判断问题在哪:
class myPromise {
// 省略
}
console.log(1);
const p1 = new myPromise((resolve, reject) => {
console.log(2);
setTimeout(() => {
console.log('A', p1.PromiseState);
resolve('成功');
console.log('B', p1.PromiseState);
console.log(4);
});
})
p1.then(
result => {
console.log('C', p1.PromiseState);
console.log('fulfilled:', result);
},
reason => {
console.log('rejected:', reason)
}
)
console.log(3);
// 输出结果
1
2
3
A pending
B fulfilled
4
结合上述执行顺序,我们先查看 第四步,执行 p1.then 时,此时 myPromise 状态为 pending ,所以并不会执行判断里的逻辑,该段逻辑执行完毕,这里和原生 Promise 有一点区别,并没有注册回调。
接着再查看 第六步,执行 setTimeout 宏任务时,myPromise 状态还没被修改,初始状态为 pending,所以此时执行 console.log('A', p1.PromiseState) 输出 A pending ; 接着执行 resolve('成功') 修改 myPromise 状态为 fulfilled ;再执行 console.log('B', p1.PromiseState),此时状态已修改,输出 B fulfilled;再执行 console.log(4) ,输出 4。这里和原生 Promise 区别在于执行 resolve 方法时,并没把 then 注册的回调推入微任务队列。
为了保持和原生 Promise 保持一致,我们需要在执行完 resolve 后再执行 then 中的回调。所以我们需要先创建 数组 ,来保存回调函数。
为什么用 数组 来保存这些回调呢?因为一个 promise 实例可能会多次 then,也就是经典的 链式调用,而且数组是先进先出的顺序。
接着我们对手写 myPromise 改造,在实例化时,创建 onFulfilledCallbacks 和 onRejectedCallbacks 两个数组,用来保存成功和失败的回调;并完善 then 方法中的代码,判断状态为 pending 时,暂时保存这两个回调;在执行 resolve 和 reject 时,遍历执行自身的 callbacks 数组,即 then 方法注册的回调函数,并逐个执行:
class myPromise {
static PENDING = 'pending';
static FULFILLED = 'fulfilled';
static REJECTED = 'rejected';
constructor(executor) {
this.PromiseState = myPromise.PENDING;
this.PromiseResult = null;
this.onFulfilledCallbacks = []; // 保存成功回调
this.onRejectedCallbacks = []; // 保存失败回调
try {
executor(this.resolve.bind(this), this.reject.bind(this));
} catch (error) {
this.reject(error)
}
}
resolve(result) {
if (this.PromiseState === myPromise.PENDING) {
this.PromiseState = myPromise.FULFILLED;
this.PromiseResult = result;
// 遍历注册成功后的回调 并逐个执行
this.onFulfilledCallbacks.forEach(callback => callback())
}
}
reject(reason) {
if (this.PromiseState === myPromise.PENDING) {
this.PromiseState = myPromise.REJECTED;
this.PromiseResult = reason;
// 遍历注册失败后的回调 并逐个执行
this.onRejectedCallbacks.forEach(callback => callback())
}
}
then(onFulfilled, onRejected) {
onFulfilled = typeof onFulfilled === 'function' ? onFulfilled : value => value;
onRejected = typeof onRejected === 'function' ? onRejected : reason => { throw reason; };
// 判断状态为 pending 时 保存 成功 和 失败 的回调
if (this.PromiseState === myPromise.PENDING) {
this.onFulfilledCallbacks.push(onFulfilled);
this.onRejectedCallbacks.push(onRejected);
}
if (this.PromiseState === myPromise.FULFILLED) {
setTimeout(() => {
onFulfilled(this.PromiseResult);
});
}
if (this.PromiseState === myPromise.REJECTED) {
setTimeout(() => {
onRejected(this.PromiseResult);
});
}
}
}
我们再来测试看下:
class myPromise {
// 省略
}
console.log(1);
const p1 = new myPromise((resolve, reject) => {
console.log(2);
setTimeout(() => {
console.log('A', p1.PromiseState);
resolve('成功');
console.log('B', p1.PromiseState);
console.log(4);
});
})
p1.then(
result => {
console.log('C', p1.PromiseState);
console.log('fulfilled:', result);
},
reason => {
console.log('rejected:', reason)
}
)
console.log(3);
// 输出结果
1
2
3
A pending
C fulfilled
fulfilled: 成功
B fulfilled
4
虽然回调中的结果 fulfilled:成功 打印出来了,但是我们还发现,代码输出顺序还是不太对,原生 Promise 中,fulfilled: 成功 是最后输出的。
这里有一个很多人忽略的小细节,要确保 onFulfilled 和 onRejected 方法异步执行,且应该在 then 方法被调用的那一轮事件循环之后的新执行栈中执行。因此,在保存成功和失败回调时也要添加 setTimeout:
class myPromise {
// 省略
then(onFulfilled, onRejected) {
onFulfilled = typeof onFulfilled === 'function' ? onFulfilled : value => value;
onRejected = typeof onRejected === 'function' ? onRejected : reason => { throw reason; };
// 判断状态为 pending 时 保存 成功 和 失败 的回调
if (this.PromiseState === myPromise.PENDING) {
// 注册回调 添加 setTimeout 确保执行顺序
this.onFulfilledCallbacks.push(() => {
setTimeout(() => {
onFulfilled(this.PromiseResult);
})
});
this.onRejectedCallbacks.push(() => {
setTimeout(() => {
onRejected(this.PromiseResult);
})
});
}
if (this.PromiseState === myPromise.FULFILLED) {
setTimeout(() => {
onFulfilled(this.PromiseResult);
});
}
if (this.PromiseState === myPromise.REJECTED) {
setTimeout(() => {
onRejected(this.PromiseResult);
});
}
}
}
我们再来测下:
class myPromise {
// 省略
}
console.log(1);
const p1 = new myPromise((resolve, reject) => {
console.log(2);
setTimeout(() => {
console.log('A', p1.PromiseState);
resolve('成功');
console.log('B', p1.PromiseState);
console.log(4);
});
})
p1.then(
result => {
console.log('C', p1.PromiseState);
console.log('fulfilled:', result);
},
reason => {
console.log('rejected:', reason)
}
)
console.log(3);
// 输出结果
1
2
3
A pending
B fulfilled
4
C fulfilled
fulfilled: 成功
当前实现完整代码:
class myPromise {
static PENDING = 'pending';
static FULFILLED = 'fulfilled';
static REJECTED = 'rejected';
constructor(executor) {
this.PromiseState = myPromise.PENDING;
this.PromiseResult = null;
this.onFulfilledCallbacks = [];
this.onRejectedCallbacks = [];
try {
executor(this.resolve.bind(this), this.reject.bind(this));
} catch (error) {
this.reject(error)
}
}
resolve(result) {
if (this.PromiseState === myPromise.PENDING) {
this.PromiseState = myPromise.FULFILLED;
this.PromiseResult = result;
this.onFulfilledCallbacks.forEach(callback => {
callback(result)
})
}
}
reject(reason) {
if (this.PromiseState === myPromise.PENDING) {
this.PromiseState = myPromise.REJECTED;
this.PromiseResult = reason;
this.onRejectedCallbacks.forEach(callback => {
callback(reason)
})
}
}
then(onFulfilled, onRejected) {
onFulfilled = typeof onFulfilled === 'function' ? onFulfilled : value => value;
onRejected = typeof onRejected === 'function' ? onRejected : reason => { throw reason; };
if (this.PromiseState === myPromise.PENDING) {
this.onFulfilledCallbacks.push(() => {
setTimeout(() => {
onFulfilled(this.PromiseResult);
})
});
this.onRejectedCallbacks.push(() => {
setTimeout(() => {
onRejected(this.PromiseResult);
})
});
}
if (this.PromiseState === myPromise.FULFILLED) {
setTimeout(() => {
onFulfilled(this.PromiseResult);
});
}
if (this.PromiseState === myPromise.REJECTED) {
setTimeout(() => {
onRejected(this.PromiseResult);
});
}
}
}
再来验证下 Promise 的 then 方法的多次调用:
const promise = new myPromise((resolve, reject) => {
setTimeout(() => {
resolve('success')
}, 1000);
})
promise.then(value => {
console.log(1)
console.log('resolve', value)
})
promise.then(value => {
console.log(2)
console.log('resolve', value)
})
promise.then(value => {
console.log(3)
console.log('resolve', value)
})
// 输出结果
1
resolve success
2
resolve success
3
resolve success
这说明我们当前的代码,已经可以实现 then 方法的多次调用。
实现 then 方法链式调用
我们先来看下原生 Promise 的 then 方法是如何链式调用的:
const p1 = new Promise((resolve, reject) => {
resolve(10)
})
p1.then(res => {
console.log('fulfilled', res)
return 2 * res
}).then(res => {
console.log('fulfilled', res)
})
// 输出结果
fulfilled 10
fulfilled 20
再举个例子:
const p2 = new Promise((resolve, reject) => {
resolve(10)
})
p2.then(res => {
console.log('fulfilled', res)
return new Promise((resolve, reject) => resolve(3 * res))
}).then(res => {
console.log('fulfilled', res)
})
// 输出结果
fulfilled 10
fulfilled 30
我们先尝试执行下当前 myPromise 是否可执行链式调用:
class myPromise {
// 省略
}
// 测试代码
const p1 = new myPromise((resolve, reject) => {
resolve(10)
})
p1.then(res => {
console.log('fulfilled', res)
return 2 * res
}).then(res => {
console.log('fulfilled', res)
})
// 输出结果
Uncaught TypeError: Cannot read properties of undefined
毫无疑问结果报错了,提示 then 方法未定义。而原生 Promise.prototype.then() 方法返回一个新的Promise实例(注意,不是原来那个 Promise 实例),因此可以采用链式写法,即 then 方法后面再调用另一个 then 方法。
Promises/A+ 规范的理解
在改造之前,我们先要对 Promises/A+ 规范的理解。想要实现 then 方法的链式调用,就必须彻底搞懂 then 方法,我们先看下规范 2.2 中是怎么描述的:
规范在 2.2.7 这样描述,译文如下:
- 2.2.7 then 方法必须返回一个 promise 对象
promise2 = promise1.then(onFulfilled, onRejected);- 2.2.7.1 如果
onFulfilled或者onRejected返回一个值x,则运行下面的 Promise 解决过程:[[Resolve]](promise2, x) - 2.2.7.2 如果
onFulfilled或者onRejected抛出一个异常e,则promise2必须拒绝执行,并返回据因e - 2.2.7.3 如果
onFulfilled不是函数且promise1成功执行,promise2必须成功执行并返回相同的值 - 2.2.7.4 如果
onRejected不是函数且promise1拒绝执行,promise2必须拒绝执行并返回相同的据因
- 2.2.7.1 如果
理解上面的 返回 部分非常重要,即:不论 promise1 被 reject 还是被 resolve 时,promise2 都会执行 Promise 解决过程:[[Resolve]](promise2, x),只有出现异常时才会被 rejected 。
注意 2.2.7.1 规范:
If either
onFulfilledoronRejectedreturns a valuex, run the Promise Resolution Procedure[[Resolve]](promise2, x).
即:如果 onFulfilled 或者 onRejected 返回一个值 x ,则运行下面的 Promise 解决过程:[[Resolve]](promise2, x)
规范在 2.3 中详细描述 Promise 解决过程 The Promise Resolution Procedure
译文如下:
◾ 2.3 Promise 解决过程
Promise 解决过程 是一个抽象的操作,其需输入一个 promise 和一个值,我们表示为 [[Resolve]](promise, x),如果 x 有 then 方法且看上去像一个 Promise ,解决程序即尝试使 promise 接受 x 的状态;否则其用 x 的值来执行 promise 。
这种 thenable 的特性使得 Promise 的实现更具有通用性:只要其暴露出一个遵循 Promises/A+ 协议的 then 方法即可;这同时也使遵循 Promises/A+ 规范的实现可以与那些不太规范但可用的实现能良好共存。
运行 [[Resolve]](promise, x) 需遵循以下步骤:
▪ 2.3.1 x 与 promise 相等
如果 promise 和 x 指向同一对象,以 TypeError 为据因拒绝执行 promise
▪ 2.3.2 x 为 Promise
如果 x 为 Promise ,则使 promise 接受 x 的状态
- 2.3.2.1 如果
x处于等待状态,promise需保持为等待状态直至x被执行或拒绝 - 2.3.2.2 如果
x处于成功状态,用相同的值执行promise - 2.3.2.3 如果
x处于拒绝状态,用相同的据因拒绝promise
▪ 2.3.3 x 为对象或函数
如果 x 为对象或者函数:
- 2.3.3.1 把
x.then赋值给then - 2.3.3.2 如果取
x.then的值时抛出错误e,则以e为据因拒绝promise - 2.3.3.3 如果
then是函数,将x作为函数的作用域this调用之。传递两个回调函数作为参数,第一个参数叫做resolvePromise,第二个参数叫做rejectPromise:- 2.3.3.3.1 如果
resolvePromise以值y为参数被调用,则运行[[Resolve]](promise, y) - 2.3.3.3.2 如果
rejectPromise以据因r为参数被调用,则以据因r拒绝promise - 2.3.3.3.3 如果
resolvePromise和rejectPromise均被调用,或者被同一参数调用了多次,则优先采用首次调用并忽略剩下的调用 - 2.3.3.3.4 如果调用
then方法抛出了异常e:- 2.3.3.3.4.1 如果
resolvePromise或rejectPromise已经被调用,则忽略之 - 2.3.3.3.4.2 否则以
e为据因拒绝promise
- 2.3.3.3.4.1 如果
- 2.3.3.4 如果
then不是函数,以x为参数执行promise
- 2.3.3.3.1 如果
▪ 2.3.4 如果 x 不为对象或者函数,以 x 为参数执行 promise
如果一个 promise 被一个循环的 thenable 链中的对象解决,而 [[Resolve]](promise, thenable) 的递归性质又使得其被再次调用,根据上述的算法将会陷入无限递归之中。算法虽不强制要求,但也鼓励施者检测这样的递归是否存在,若检测到存在则以一个可识别的 TypeError 为据因来拒绝 promise。
Promises/A+ 规范的总结
基于规范的描述,我们得到以下几点:
then方法本身会返回一个新的Promise对象,返回一个新的 Promise 以后它就有自己的then方法,这样就能实现无限的链式- 不论
promise1被resolve()还是被reject()时,promise2都会执行Promise 解决过程:[[Resolve]](promise2, x)
在手写这里我们把这个 Promise 解决过程:[[Resolve]](promise2, x) 命名为 resolvePromise() 方法,参数为 (promise2, x, resolve, reject) 即:
function resolvePromise(promise2, x, resolve, reject) {}
resolvePromise()各参数的意义:
/**
* 针对resolve()和reject()中不同值情况 进行处理
* @param {promise} promise2 promise1.then方法返回的新的 promise 对象
* @param {[type]} x promise1 中 onFulfilled 或 onRejected 的返回值
* @param {[type]} resolve promise2 的 resolve 方法
* @param {[type]} reject promise2 的 reject 方法
*/
function resolvePromise(promise2, x, resolve, reject) {}
其实,这个resolvePromise(promise2, x, resolve, reject) 即 Promise 解决过程:[[Resolve]](promise2, x) 就是针对 resolve() 和 reject() 中不同值情况进行处理。
resolve()和reject() 返回的 x 值的几种情况:
- 普通值
- Promise 对象
- thenable 对象/函数
下面我们就根据总结的两点,结合 Promises/A+ 规范 来实现 then 方法的链式调用
Promises/A+ 规范改造过程
- 2.2.7规范 then 方法必须返回一个 promise 对象:
class myPromise {
// 省略
then(onFulfilled, onRejected) {
onFulfilled = typeof onFulfilled === 'function' ? onFulfilled : value => value;
onRejected = typeof onRejected === 'function' ? onRejected : reason => { throw reason; };
// 返回一个 promise 对象
const promise2 = new myPromise((resolve, reject) => {
if (this.PromiseState === myPromise.FULFILLED) {
setTimeout(() => {
onFulfilled(this.PromiseResult);
});
} else if (this.PromiseState === myPromise.REJECTED) {
setTimeout(() => {
onRejected(this.PromiseResult);
});
} else if (this.PromiseState === myPromise.PENDING) {
this.onFulfilledCallbacks.push(() => {
setTimeout(() => {
onFulfilled(this.PromiseResult);
});
});
this.onRejectedCallbacks.push(() => {
setTimeout(() => {
onRejected(this.PromiseResult);
});
});
}
})
return promise2
}
}
- 2.2.7.1规范 如果
onFulfilled或者onRejected返回一个值x,则运行下面的 Promise 解决过程:[[Resolve]](promise2, x)
class myPromise {
// 省略
then(onFulfilled, onRejected) {
onFulfilled = typeof onFulfilled === 'function' ? onFulfilled : value => value;
onRejected = typeof onRejected === 'function' ? onRejected : reason => { throw reason; };
// 返回一个 promise 对象
const promise2 = new myPromise((resolve, reject) => {
if (this.PromiseState === myPromise.FULFILLED) {
setTimeout(() => {
// onFulfilled 返回一个值 X 执行 resolvePromise 解决过程
let x = onFulfilled(this.PromiseResult);
resolvePromise(promise2, x, resolve, reject);
});
} else if (this.PromiseState === myPromise.REJECTED) {
setTimeout(() => {
// onRejected 返回一个值 X 执行 resolvePromise 解决过程
let x = onRejected(this.PromiseResult);
resolvePromise(promise2, x, resolve, reject);
});
} else if (this.PromiseState === myPromise.PENDING) {
this.onFulfilledCallbacks.push(() => {
setTimeout(() => {
onFulfilled(this.PromiseResult);
});
});
this.onRejectedCallbacks.push(() => {
setTimeout(() => {
onRejected(this.PromiseResult);
});
});
}
})
return promise2
}
}
/**
* Promise 解决过程
* 针对 resolve() 和 reject() 中不同值情况 进行处理
* @param {promise} promise2 promise1.then 方法返回的新的 promise 对象
* @param {[type]} x promise1 中 onFulfilled 或 onRejected 的返回值
* @param {[type]} resolve promise2 的 resolve 方法
* @param {[type]} reject promise2 的 reject 方法
*/
function resolvePromise(promise2, x, resolve, reject) {}
- 2.2.7.2规范 如果
onFulfilled或者onRejected抛出一个异常e,则promise2必须拒绝执行,并返回拒因e
class myPromise {
// 省略
then(onFulfilled, onRejected) {
onFulfilled = typeof onFulfilled === 'function' ? onFulfilled : value => value;
onRejected = typeof onRejected === 'function' ? onRejected : reason => { throw reason; };
// 返回一个 promise 对象
const promise2 = new myPromise((resolve, reject) => {
if (this.PromiseState === myPromise.FULFILLED) {
setTimeout(() => {
try {
// onFulfilled 返回一个值 X 执行 resolvePromise 解决过程
let x = onFulfilled(this.PromiseResult);
resolvePromise(promise2, x, resolve, reject);
} catch(e) {
reject(e); // 捕获前面 onFulfilled 中抛出的异常
}
});
} else if (this.PromiseState === myPromise.REJECTED) {
setTimeout(() => {
try {
// onRejected 返回一个值 X 执行 resolvePromise 解决过程
let x = onRejected(this.PromiseResult);
resolvePromise(promise2, x, resolve, reject);
} catch(e) {
reject(e); // 捕获前面 onRejected 中抛出的异常
}
});
} else if (this.PromiseState === myPromise.PENDING) {
this.onFulfilledCallbacks.push(() => {
setTimeout(() => {
onFulfilled(this.PromiseResult);
});
});
this.onRejectedCallbacks.push(() => {
setTimeout(() => {
onRejected(this.PromiseResult);
});
});
}
})
return promise2
}
}
function resolvePromise(promise2, x, resolve, reject) {}
另外,结合 2.2.7.1 和 2.2.7.2 规范,我们还需处理 pending 状态的逻辑
class myPromise {
// 省略
then(onFulfilled, onRejected) {
onFulfilled = typeof onFulfilled === 'function' ? onFulfilled : value => value;
onRejected = typeof onRejected === 'function' ? onRejected : reason => { throw reason; };
const promise2 = new myPromise((resolve, reject) => {
if (this.PromiseState === myPromise.FULFILLED) {
setTimeout(() => {
try {
let x = onFulfilled(this.PromiseResult);
resolvePromise(promise2, x, resolve, reject);
} catch(e) {
reject(e);
}
});
} else if (this.PromiseState === myPromise.REJECTED) {
setTimeout(() => {
try {
let x = onRejected(this.PromiseResult);
resolvePromise(promise2, x, resolve, reject);
} catch(e) {
reject(e);
}
});
} else if (this.PromiseState === myPromise.PENDING) {
// 根据 2.2.7.1 和 2.2.7.2 规范 处理
this.onFulfilledCallbacks.push(() => {
setTimeout(() => {
try {
let x = onFulfilled(this.PromiseResult);
resolvePromise(promise2, x, resolve, reject);
} catch(e) {
reject(e);
}
});
});
this.onRejectedCallbacks.push(() => {
setTimeout(() => {
try {
let x = onRejected(this.PromiseResult);
resolvePromise(promise2, x, resolve, reject);
} catch(e) {
reject(e);
}
});
});
}
})
return promise2
}
}
function resolvePromise(promise2, x, resolve, reject) {}
- 2.2.7.3规范 如果
onFulfilled不是函数且promise1成功执行,promise2必须成功执行并返回相同的值
// 在 fulfilled 和 pending 状态中 添加 判断逻辑
class myPromise {
// 省略
then(onFulfilled, onRejected) {
onFulfilled = typeof onFulfilled === 'function' ? onFulfilled : value => value;
onRejected = typeof onRejected === 'function' ? onRejected : reason => { throw reason; };
const promise2 = new myPromise((resolve, reject) => {
if (this.PromiseState === myPromise.FULFILLED) {
setTimeout(() => {
try {
// onFulfilled 不是 函数
if(typeof onFulfilled !== 'function') {
resolve(this.PromiseResult);
}else {
let x = onFulfilled(this.PromiseResult);
resolvePromise(promise2, x, resolve, reject);
}
} catch(e) {
reject(e);
}
});
} else if (this.PromiseState === myPromise.REJECTED) {
setTimeout(() => {
try {
let x = onRejected(this.PromiseResult);
resolvePromise(promise2, x, resolve, reject);
} catch(e) {
reject(e);
}
});
} else if (this.PromiseState === myPromise.PENDING) {
this.onFulfilledCallbacks.push(() => {
setTimeout(() => {
try {
// onFulfilled 不是 函数
if(typeof onFulfilled !== 'function') {
resolve(this.PromiseResult);
}else {
let x = onFulfilled(this.PromiseResult);
resolvePromise(promise2, x, resolve, reject);
}
} catch(e) {
reject(e);
}
});
});
this.onRejectedCallbacks.push(() => {
setTimeout(() => {
try {
let x = onRejected(this.PromiseResult);
resolvePromise(promise2, x, resolve, reject);
} catch(e) {
reject(e);
}
});
});
}
})
return promise2
}
}
function resolvePromise(promise2, x, resolve, reject) {}
- 2.2.7.4规范 如果
onRejected不是函数且promise1拒绝执行,promise2必须拒绝执行并返回相同的据因
// 在 rejected 和 pending 状态中 添加 判断逻辑
class myPromise {
// 省略
then(onFulfilled, onRejected) {
onFulfilled = typeof onFulfilled === 'function' ? onFulfilled : value => value;
onRejected = typeof onRejected === 'function' ? onRejected : reason => { throw reason; };
const promise2 = new myPromise((resolve, reject) => {
if (this.PromiseState === myPromise.FULFILLED) {
setTimeout(() => {
try {
if(typeof onFulfilled !== 'function') {
resolve(this.PromiseResult);
}else {
let x = onFulfilled(this.PromiseResult);
resolvePromise(promise2, x, resolve, reject);
}
} catch(e) {
reject(e);
}
});
} else if (this.PromiseState === myPromise.REJECTED) {
setTimeout(() => {
try {
// onRejected 不是 函数
if(typeof onRejected !== 'function') {
reject(this.PromiseResult);
} else {
let x = onRejected(this.PromiseResult);
resolvePromise(promise2, x, resolve, reject);
}
} catch(e) {
reject(e);
}
});
} else if (this.PromiseState === myPromise.PENDING) {
this.onFulfilledCallbacks.push(() => {
setTimeout(() => {
try {
if(typeof onFulfilled !== 'function') {
resolve(this.PromiseResult);
}else {
let x = onFulfilled(this.PromiseResult);
resolvePromise(promise2, x, resolve, reject);
}
} catch(e) {
reject(e);
}
});
});
this.onRejectedCallbacks.push(() => {
setTimeout(() => {
try {
// onRejected 不是 函数
if(typeof onRejected !== 'function') {
reject(this.PromiseResult);
} else {
let x = onRejected(this.PromiseResult);
resolvePromise(promise2, x, resolve, reject);
}
} catch(e) {
reject(e);
}
});
});
}
})
return promise2
}
}
function resolvePromise(promise2, x, resolve, reject) {}
根据 2.2.7.3 和 2.2.7.4 规范 对 onFulfilled 和 onRejected 不是函数的情况已经做了更详细的描述,根据描述我们对 onFulfilled 和 onRejected 引入了新的参数校验,所以之前的参数校验就可以删除:
class myPromise {
// 省略
then(onFulfilled, onRejected) {
// 以下代码 删除
/* onFulfilled = typeof onFulfilled === 'function' ? onFulfilled : value => value;
onRejected = typeof onRejected === 'function' ? onRejected : reason => { throw reason; };
*/
}
}
function resolvePromise(promise2, x, resolve, reject) {}
实现 resolvePromise 方法
- 2.3.1规范 如果
promise和x指向同一对象,以TypeError为据因拒绝执行promise
function resolvePromise(promise2, x, resolve, reject) {
if (x === promise2) {
// 抛出错误 catch 捕获
throw new TypeError('Chaining cycle detected for promise');
// 也可以直接 reject 错误 两种写法都可
// return reject(new TypeError('Chaining cycle detected for promise'));
}
}
在这里我们只需要抛出一个 TypeError 的异常即可,因为调用 resolvePromise 方法外层的 try...catch 会捕获这个异常,然后 以 TypeError 为据因拒绝执行 promise。
我们来看下下面的例子:
const promise = new Promise((resolve, reject) => {
resolve(100)
})
const p1 = promise.then(value => {
console.log(value)
return p1
})
// 输出结果
100
Uncaught (in promise) TypeError: Chaining cycle detected for promise #<Promise>
可以看出原生 Promise 执行后,报类型错误。
- 2.3.2规范 如果
x为 Promise ,则使promise接受x的状态
class myPromise {}
function resolvePromise(promise2, x, resolve, reject) {
if (x === promise2) {
throw new TypeError('Chaining cycle detected for promise');
}
if (x instanceof myPromise) {
/**
* 如果 x 为 Promise ,则使 promise2 接受 x 的状态
* 也就是继续执行 x,如果执行的时候拿到一个 y,则继续解析 y
* 比如 p1.then(res => { reuturn new myPromise((resolve, reject) => { resolve('测试') }) })
* 此时 y 等于 测试
*/
x.then(y => {
resolvePromise(promise2, y, resolve, reject)
}, reject);
}
}
- 2.3.3规范 如果
x为对象或者函数
在判断x是对象或函数时,x 不能是 null,因为 typeof null 的值也为 object
if (x != null && ((typeof x === 'object' || (typeof x === 'function'))))
- 2.3.4规范 如果
x不为对象或者函数,以x为参数执行promise
function resolvePromise(promise2, x, resolve, reject) {
if (x === promise2) {
throw new TypeError('Chaining cycle detected for promise');
}
if (x instanceof myPromise) {
/**
* 2.3.2 如果 x 为 Promise ,则使 promise2 接受 x 的状态
* 也就是继续执行 x,如果执行的时候拿到一个 y,则继续解析 y
* 比如 p1.then(res => { reuturn new myPromise((resolve, reject) => { resolve('测试') }) })
* 此时 y 等于 测试
*/
x.then(y => {
resolvePromise(promise2, y, resolve, reject)
}, reject);
} else if (x !== null && ((typeof x === 'object' || (typeof x === 'function')))) {
// 2.3.3 如果 x 为对象或函数
try {
// 2.3.3.1 把 x.then 赋值给 then
var then = x.then;
} catch (e) {
// 2.3.3.2 如果取 x.then 的值时抛出错误 e ,则以 e 为据因拒绝 promise
return reject(e);
}
/**
* 2.3.3.3
* 如果 then 是函数,则将 x 作为函数的作用域 this 调用
* 传递两个回调函数作为参数
* 第一个参数 resolvePromise ,第二个参数 rejectPromise
*/
if (typeof then === 'function') {
/**
* 2.3.3.3.3 如果 resolvePromise 和 rejectPromise 均被调用,
* 或者被同一参数调用了多次,则优先采用首次调用并忽略剩下的调用
*/
let called = false; // 避免多次调用
try {
then.call(
x,
// 2.3.3.3.1 如果 resolvePromise 以值 y 为参数被调用,则运行 [[Resolve]](promise, y)
y => {
if (called) return; // 避免多次调用
called = true;
resolvePromise(promise2, y, resolve, reject);
},
// 2.3.3.3.2 如果 rejectPromise 以据因 r 为参数被调用,则以据因 r 拒绝 promise
r => {
if (called) return; // 避免多次调用
called = true;
reject(r);
}
)
} catch (e) {
/**
* 2.3.3.3.4 如果调用 then 方法抛出了异常 e
* 2.3.3.3.4.1 如果 resolvePromise 或 rejectPromise 已经被调用,则忽略
*/
if (called) return;
called = true;
/**
* 2.3.3.3.4.2 否则以 e 为据因拒绝 promise
*/
reject(e);
}
} else {
// 2.3.3.4 如果 then 不是函数,以 x 为参数执行 promise
resolve(x);
}
} else {
// 2.3.4 如果 x 不为对象或者函数,以 x 为参数执行 promise
return resolve(x);
}
}
完整的 Promises/A+ 实现
class myPromise {
// 创建 promise 的三个状态
static PENDING = 'pending'; // 待定
static FULFILLED = 'fulfilled'; // 已成功
static REJECTED = 'rejected'; // 已拒绝
// 构造函数:通过 new 命令生成对象实例时,自动调用类的构造函数
constructor(executor) { // 给类的构造方法 constructor 添加一个参数 executor
this.PromiseState = myPromise.PENDING; // 指定 Promise 对象的状态属性 PromiseState,初始值为pending
this.PromiseResult = null; // 指定 Promise 对象的结果 PromiseResult
this.onFulfilledCallbacks = []; // 保存成功回调
this.onRejectedCallbacks = []; // 保存失败回调
try {
/**
* executor()传入 resolve 和 reject
* 实例创建完 外部调用 resolve() 和reject(),会导致 this 指向丢失
* 这里使用 bind 修改 this 指向
* new 对象实例时,自动执行 executor()
*/
executor(this.resolve.bind(this), this.reject.bind(this));
} catch (error) {
/**
* 生成实例时(执行 resolve 和 reject ),如果报错,
* 就把错误信息传入给 reject() 方法,并且直接执行 reject() 方法
*/
this.reject(error)
}
}
resolve(result) { // result 为成功状态时接收的终值
// 只能由 pending 状态 => fulfilled 状态 (避免调用多次 resolve reject)
if (this.PromiseState === myPromise.PENDING) {
this.PromiseState = myPromise.FULFILLED;
this.PromiseResult = result;
/**
* 在执行 resolve 或者 reject 时,遍历自身的 callbacks 数组,
* onFulfilledCallbacks 由 then 方法执行时 注册的成功 回调方法
*/
this.onFulfilledCallbacks.forEach(callback => callback())
}
}
reject(reason) { // reason为拒绝态时接收的终值
// 只能由pending 状态 => rejected 状态 (避免调用多次resolve reject)
if (this.PromiseState === myPromise.PENDING) {
this.PromiseState = myPromise.REJECTED;
this.PromiseResult = reason;
this.onRejectedCallbacks.forEach(callback => callback())
}
}
/**
* [注册fulfilled状态/rejected状态对应的回调函数]
* @param {function} onFulfilled fulfilled 状态时 执行的函数
* @param {function} onRejected rejected 状态时 执行的函数
* @returns {function} newPromsie 返回一个新的 promise 对象
*/
then(onFulfilled, onRejected) {
// 2.2.7规范 then 方法必须返回一个 promise 对象
let promise2 = new myPromise((resolve, reject) => {
if (this.PromiseState === myPromise.FULFILLED) {
/**
* 为什么这里要加定时器setTimeout?
* 2.2.4规范 onFulfilled 和 onRejected 只有在执行环境堆栈仅包含平台代码时才可被调用 注1
* 这里的平台代码指的是引擎、环境以及 promise 的实施代码。
* 实践中要确保 onFulfilled 和 onRejected 方法异步执行,且应该在 then 方法被调用的那一轮事件循环之后的新执行栈中执行。
* 这个事件队列可以采用“宏任务(macro-task)”机制,比如setTimeout 或者 setImmediate; 也可以采用“微任务(micro-task)”机制来实现, 比如 MutationObserver 或者process.nextTick。
*/
setTimeout(() => {
try {
if (typeof onFulfilled !== 'function') {
// 2.2.7.3规范 如果 onFulfilled 不是函数且 promise1 成功执行, promise2 必须成功执行并返回相同的值
resolve(this.PromiseResult);
} else {
// 2.2.7.1规范 如果 onFulfilled 或者 onRejected 返回一个值 x ,则运行下面的 Promise 解决过程:[[Resolve]](promise2, x),即运行 resolvePromise()
let x = onFulfilled(this.PromiseResult);
resolvePromise(promise2, x, resolve, reject);
}
} catch (e) {
// 2.2.7.2规范 如果 onFulfilled 或者 onRejected 抛出一个异常 e ,则 promise2 必须拒绝执行,并返回拒因 e
reject(e); // 捕获前面 onFulfilled 中抛出的异常
}
});
} else if (this.PromiseState === myPromise.REJECTED) {
setTimeout(() => {
try {
if (typeof onRejected !== 'function') {
// 2.2.7.4规范 如果 onRejected 不是函数且 promise1 拒绝执行, promise2 必须拒绝执行并返回相同的据因
reject(this.PromiseResult);
} else {
let x = onRejected(this.PromiseResult);
resolvePromise(promise2, x, resolve, reject);
}
} catch (e) {
reject(e) // 捕获前面 onRejected 中抛出的异常
}
});
} else if (this.PromiseState === myPromise.PENDING) {
// pending 状态保存的 onFulfilled() 和 onRejected() 回调也要符合 2.2.7.1,2.2.7.2,2.2.7.3 和 2.2.7.4 规范
this.onFulfilledCallbacks.push(() => {
setTimeout(() => {
try {
if (typeof onFulfilled !== 'function') {
resolve(this.PromiseResult);
} else {
let x = onFulfilled(this.PromiseResult);
resolvePromise(promise2, x, resolve, reject);
}
} catch (e) {
reject(e);
}
});
});
this.onRejectedCallbacks.push(() => {
setTimeout(() => {
try {
if (typeof onRejected !== 'function') {
reject(this.PromiseResult);
} else {
let x = onRejected(this.PromiseResult);
resolvePromise(promise2, x, resolve, reject);
}
} catch (e) {
reject(e);
}
});
});
}
})
return promise2
}
}
/**
* 针对resolve()和reject()中不同值情况 进行处理
* @param {promise} promise2 promise1.then 方法返回的新的 promise 对象
* @param {[type]} x promise1 中 onFulfilled 或 onRejected 的返回值
* @param {[type]} resolve promise2的 resolve 方法
* @param {[type]} reject promise2的 reject 方法
*/
function resolvePromise(promise2, x, resolve, reject) {
// 2.3.1规范 如果 promise 和 x 指向同一对象,以 TypeError 为据因拒绝执行 promise
if (x === promise2) {
throw new TypeError('Chaining cycle detected for promise');
}
if (x instanceof myPromise) {
/**
* 2.3.2 如果 x 为 Promise ,则使 promise2 接受 x 的状态
* 也就是继续执行x,如果执行的时候拿到一个y,还要继续解析y
* 比如 p1.then(res => { reuturn new myPromise((resolve, reject) => { resolve('测试') }) })
* 此时 y 等于 测试
*/
x.then(y => {
resolvePromise(promise2, y, resolve, reject)
}, reject);
} else if (x !== null && ((typeof x === 'object' || (typeof x === 'function')))) {
// 2.3.3 如果 x 为对象或函数
try {
// 2.3.3.1 把 x.then 赋值给 then
var then = x.then;
} catch (e) {
// 2.3.3.2 如果取 x.then 的值时抛出错误 e ,则以 e 为据因拒绝 promise
return reject(e);
}
/**
* 2.3.3.3
* 如果 then 是函数,将 x 作为函数的作用域 this 调用
* 传递两个回调函数作为参数
* 第一个参数 resolvePromise ,第二个参数 rejectPromise
*/
if (typeof then === 'function') {
// 2.3.3.3.3 如果 resolvePromise 和 rejectPromise 均被调用,或者被同一参数调用了多次,则优先采用首次调用并忽略剩下的调用
let called = false; // 避免多次调用
try {
then.call(
x,
// 2.3.3.3.1 如果 resolvePromise 以值 y 为参数被调用,则运行 [[Resolve]](promise, y)
y => {
if (called) return;
called = true;
resolvePromise(promise2, y, resolve, reject);
},
// 2.3.3.3.2 如果 rejectPromise 以据因 r 为参数被调用,则以据因 r 拒绝 promise
r => {
if (called) return;
called = true;
reject(r);
}
)
} catch (e) {
/**
* 2.3.3.3.4 如果调用 then 方法抛出了异常 e
* 2.3.3.3.4.1 如果 resolvePromise 或 rejectPromise 已经被调用,则忽略之
*/
if (called) return;
called = true;
// 2.3.3.3.4.2 否则以 e 为据因拒绝 promise
reject(e);
}
} else {
// 2.3.3.4 如果 then 不是函数,以 x 为参数执行 promise
resolve(x);
}
} else {
// 2.3.4 如果 x 不为对象或者函数,以 x 为参数执行 promise
return resolve(x);
}
}
我们再来测试下:
class myPromise {}
const p1 = new myPromise((resolve, reject) => {
resolve(100)
})
p1.then(res => {
return 200
}).then(res => {
console.log(res)
})
// 输出结果
200
p1.then(null).then(res => {
console.log(res)
})
// 输出结果
100
const p2 = p1.then(res => {
return p2
})
p2.then(null, err => {
console.log(err)
})
// 输出结果
TypeError: Chaining cycle detected for promise
at resolvePromise (index.html:157:23)
at index.html:89:37
p1.then(res => {
return new myPromise((resolve, reject) => {
resolve(300)
})
}).then(res => {
console.log(res)
})
// 输出结果
300
Promise 测试
如何证明我们写的 myPromise 符合 Promises/A+ 规范呢?
我们可以使用 Promises/A+ 官方的测试工具 promises-aplus-tests 来对我们的 myPromise 进行测试
- 安装
promises-aplus-tests:
npm install promises-aplus-tests -D
安装完后,目录结构如下:
- test
- node_modules
- myPromise.js
- package.json
- 修改
myPromise.js文件,使用CommonJS对外暴露myPromise类
class myPromise {}
function resolvePromise(promise2, x, resolve, reject) {}
module.exports = myPromise;
- 实现静态方法
deferred
使用 promises-aplus-tests 这个工具测试,必须实现一个静态方法 deferred(),官方对这个方法的定义如下:
大致译文如下:
我们要给自己手写的 myPromise 上实现一个静态方法 deferred(),该方法要返回一个包含 { promise, resolve, reject } 的对象:
promise是一个处于pending状态的 Promsieresolve(value)用value解决上面那个promisereject(reason)用reason拒绝上面那个promise
deferred 实现:
class myPromise {}
function resolvePromise(promise2, x, resolve, reject) { }
myPromise.deferred = function () {
let result = {};
result.promise = new myPromise((resolve, reject) => {
result.resolve = resolve;
result.reject = reject;
});
return result;
}
module.exports = myPromise;
- 配置 package.json
// package.json
{
"devDependencies": {
"promises-aplus-tests": "^2.1.2"
},
"scripts": {
"test": "promises-aplus-tests myPromise"
}
}
- 执行测试
npm run test
测试结果完美通过官方 872 个测试用例🎉🎉🎉
参考
从一道让我失眠的 Promise 面试题开始,深入分析 Promise 实现细节