手把手带你实现符合Promise/A+规范的Promise
如果你还没有使用Promise的经验,那我建议你先阅读博主之前关于《带你快速入门ES6中的Promise对象》的文章。
本篇博文主要带领大家一步步实现Promise/A+规范的Promise,所以前提是你需要对Promise有一定的基础,然后通过一步步实现自己的Promise,才能更加深入的了解到Promise底层的实现原理。
什么是Promise/A+规范?
首先什么是Promise/A+规范?官网上是这么描述的:
An open standard for sound, interoperable JavaScript promises—by implementers, for implementers.
简而言之它是一套标准,ES中标准的Promise实现就是遵守这一套标准的。当然还有其他的Promise库,他们也是遵守这一套标准的。所以这套标准告诉我们一个规范的Promise需要满足哪些条件。具体的条件可以在官网中获取,这里就不多赘述了。官网链接:https://promisesaplus.com/
一步步实现自定义Promise
我们自己实现的自定义Promise首先肯定是符合Promise/A+规范的,然后我们会参考ES6的Promise进行对比来一步步实现自己的Promise。
构造函数
首先我们来看规范中怎么定义一个Promise
“promise” is an object or function with a then method whose behavior conforms to this specification.
然后我们来看,ES6中是怎么创建一个Promise的?
let promise = new Promise(function(resolve, reject) {
/*
如果操作成功,调用resolve并传入value --> resolve(value)
如果操作失败,调用reject并传入reason --> rejectreason)
*/
})
可以看到ES6中是通过new一个Promise,然后传入一个方法,方法有两个参数(resolve, reject)
,当异步操作成功,调用resolve
,异步操作失败调用reject
。 所以这两个参数实际上是两个方法,并确实带一个参数的方法。
然后在定义中还说了,一个promise是需要带一个then
方法的,我们可以看看ES6中的then
方法
这个then
方法接受两个参数:
promise.then(onFulfilled, onRejected)
接下来我们可以模仿着,写一下我们自定义Promise的构造函数
和then
方法
function Promise(executor){ //构造函数传入一个方法,方法有两个参数,两个参数都是一个方法并且带一个传入参数
}
Promise.prototype.then = function (onFulfilled, onRejected){
}
resolve 与 reject的构建与基础实现
如果你有一定的基础,你就知道Promise是有三种状态的pending, fulfilled, rejected
可以看规范中对Promise states的定义:
A promise must be in one of three states: pending, fulfilled, or rejected.
- 2.1.1. When pending, a promise:
- 2.1.1.1. may transition to either the fulfilled or rejected state.
- 2.1.2. When fulfilled, a promise:
- 2.1.2.1. must not transition to any other state.
- 2.1.2.2. must have a value, which must not change.
- 2.1.3. When rejected, a promise:
- 2.1.3.1. must not transition to any other state.
- 2.1.3.2. must have a reason, which must not change.
然后我们看到ES6中的Promise对象上是会有一个PromiseState
的属性的,这个属性只有三种状态pending, fulfilled, rejected
而且当一个Promise实例化出来的时候,状态是pending
,然后通过调用resolve
方法或者reject
方法去改变Promise的状态。
所以我们需要我们自定义的Promise加一个属性,然后默认初始化的时候是pending
function Promise(executor) { //构造函数传入一个方法,方法有两个参数,两个参数都是一个方法并且带一个传入参数
//添加属性
this.PromiseState = 'pending';
}
Promise.prototype.then = function (onFulfilled, onRejected) {
}
然后我们继续来完善一些细节,首先我们看到ES6的Promise中还有一个属性就是PromiseResult
,他是用来存储这个Promise的值,默认是undefined
const PENDING = 'pending'
const FULFILLED = 'fulfilled'
const REJECTED = 'rejected'
function Promise(executor) { //构造函数传入一个方法,方法有两个参数,两个参数都是一个方法并且带一个传入参数
//添加属性
this.PromiseState = PENDING
this.PromiseResult = undefined
}
Promise.prototype.then = function (onFulfilled, onRejected) {
}
然后还有几个细节就是,Promise在实例化完成之后就会立马执行,比如ES6中的Promise
所以我们在Promise的构造函数中需要调用,传进来的executor
方法。但是这个executor
方法还有两个参数resolve,reject
,我们需要给他传进去,所以我们还需要定义一下这两个方法。
const PENDING = 'pending'
const FULFILLED = 'fulfilled'
const REJECTED = 'rejected'
function Promise(executor) { //构造函数传入一个方法,方法有两个参数,两个参数都是一个方法并且带一个传入参数
//添加属性
this.PromiseState = PENDING
this.PromiseResult = undefined
//resolve 函数
function resolve(data) {
}
//reject 函数
function reject(data) {
}
//同步调用『执行器函数』
executor(resolve, reject)
}
Promise.prototype.then = function (onFulfilled, onRejected) {
}
接下来,我们需要去实现resolve
方法和reject
方法。
首先我们知道在ES6的Promise中,可以通过resolve
方法把Promise从pending
变成fulfilled
; 可以通过通过reject
方法把Promise从pending
变成rejected
, 并且传入方法的参数,最后会变成Promise的PromiseResult
属性的值。如下:
所以接下来我们就可以实现resolve
方法和reject
方法了。
const PENDING = 'pending'
const FULFILLED = 'fulfilled'
const REJECTED = 'rejected'
function Promise(executor) { //构造函数传入一个方法,方法有两个参数,两个参数都是一个方法并且带一个传入参数
//添加属性
this.PromiseState = PENDING
this.PromiseResult = undefined
//保存实例对象的 this 的值
//此处可以不写,但是下面function方法需要改为箭头函数,否则function默认指向是window
const self = this
//resolve 函数
function resolve(data) {
//1. 修改对象的状态 (promiseState)
self.PromiseState = FULFILLED; // fulfilled
//2. 设置对象结果值 (promiseResult)
self.PromiseResult = data;
}
//reject 函数
function reject(data) {
//1. 修改对象的状态 (promiseState)
self.PromiseState = REJECTED; // rejected
//2. 设置对象结果值 (promiseResult)
self.PromiseResult = data;
}
//同步调用『执行器函数』
executor(resolve, reject)
}
Promise.prototype.then = function (onFulfilled, onRejected) {
}
接下来我们可以测试一下实例化Promise对象的时候有没有问题了,我们写这样的一段测试代码,来看看控制台的输出。
let p1 = new Promise((resolve, reject) => {
console.log("同步执行1")
resolve('success')
})
let p2 = new Promise((resolve, reject) => {
console.log("同步执行2")
reject('failed')
})
console.log("同步执行3")
console.log(p1)
console.log(p2)
throw 抛出异常改变状态
我们在学习promise的时候也知道,当在promise中的异步操作发生异常的时候,最后的状态会变成rejected
,而且最后的PromiseResult
的值,是我们抛出的这个异常对象。但我们现在的代码很明显没有这一步的操作。
下面是ES6中Promise的执行结果:
接下来我们就来处理这一步,所以我们需要捕获executor(resolve, reject)
执行的异常,然后把当前的状态变成rejected
const PENDING = 'pending'
const FULFILLED = 'fulfilled'
const REJECTED = 'rejected'
function Promise(executor) { //构造函数传入一个方法,方法有两个参数,两个参数都是一个方法并且带一个传入参数
//添加属性
this.PromiseState = PENDING
this.PromiseResult = undefined
//保存实例对象的 this 的值
//此处可以不写,但是下面function方法需要改为箭头函数,否则function默认指向是window
const self = this
//resolve 函数
function resolve(data) {
//1. 修改对象的状态 (promiseState)
self.PromiseState = FULFILLED; // fulfilled
//2. 设置对象结果值 (promiseResult)
self.PromiseResult = data;
}
//reject 函数
function reject(data) {
//1. 修改对象的状态 (promiseState)
self.PromiseState = REJECTED; // rejected
//2. 设置对象结果值 (promiseResult)
self.PromiseResult = data;
}
try {
//同步调用『执行器函数』
executor(resolve, reject)
} catch (error) {
//修改 promise 对象状态为『失败』
reject(error)
}
}
Promise.prototype.then = function (onFulfilled, onRejected) {
}
接下来我们来测试这一部分的功能
let p3 = new Promise((resolve, reject) => {
throw new Error('error!');
})
控制台输出如下:
Promise的状态一旦改变,就不会再变
一旦状态改变,就不会再变,任何时候都可以得到这个结果。Promise对象的状态改变,只有两种可能:从pending
变为fulfilled
和从pending
变为rejected
。
比如在ES6中执行如下代码,可以看出来,一旦Promise的状态改变了,就不会在变了
但是我们现在的实现中没有做这样的限制,所以现在我们需要加上这个逻辑,也就是只有在pending
状态下,才能变为fulfilled
或者rejected
const PENDING = 'pending'
const FULFILLED = 'fulfilled'
const REJECTED = 'rejected'
function Promise(executor) { //构造函数传入一个方法,方法有两个参数,两个参数都是一个方法并且带一个传入参数
//添加属性
this.PromiseState = PENDING
this.PromiseResult = undefined
//保存实例对象的 this 的值
//此处可以不写,但是下面function方法需要改为箭头函数,否则function默认指向是window
const self = this
//resolve 函数
function resolve(data) {
if (self.PromiseState !== PENDING) { //判断状态,保证promise的状态只能从'pending'变为'fulfilled'
return;
}
//1. 修改对象的状态 (promiseState)
self.PromiseState = FULFILLED; // fulfilled
//2. 设置对象结果值 (promiseResult)
self.PromiseResult = data;
}
//reject 函数
function reject(data) {
//判断状态
if (self.PromiseState !== PENDING) { //判断状态,保证promise的状态只能从'pending'变为'rejected'
return;
}
//1. 修改对象的状态 (promiseState)
self.PromiseState = REJECTED; // rejected
//2. 设置对象结果值 (promiseResult)
self.PromiseResult = data;
}
try {
//同步调用『执行器函数』
executor(resolve, reject)
} catch (error) {
//修改 promise 对象状态为『失败』
reject(error)
}
}
Promise.prototype.then = function (onFulfilled, onRejected) {
}
then 方法执行回调基础实现
promise的then(onFulfilled, onRejected)
方法当promise的状态是fulfilled
的时候就会调用onFulfilled
方法,当状态是rejected
的时候就会调用onRejected
方法,并且会把PromiseResult
的结果传入。
所以下面我们需要实现then
方法
Promise.prototype.then = function (onFulfilled, onRejected) {
//调用回调函数 PromiseState
if (this.PromiseState === FULFILLED) {
onFulfilled(this.PromiseResult)
}
if (this.PromiseState === REJECTED) {
onRejected(this.PromiseResult)
}
}
我们可以进行测试看看
let p1 = new Promise((resolve, reject) => {
resolve('success')
})
p1.then(function (resolved) {
console.log(resolved)
}, function (rejected) {
console.log(rejected)
})
let p2 = new Promise((resolve, reject) => {
reject('failed')
})
p2.then(function (resolved) {
console.log(resolved)
}, function (rejected) {
console.log(rejected)
})
但上面的方法存在一个问题,就是当promise里面的操作是异步执行之后才进行resolve
方法或者reject
方法的话,在执行then
就会有问题,因为这个时候PromiseState
的状态还是pending
我们可以测试看看
let p1 = new Promise((resolve, reject) => {
setTimeout(() => resolve('success'), 0) // resolve方法会异步执行,所以会先执行下面的then方法,此时当前的状态是pending
})
p1.then(function (resolved) { //then方法执行,判断当前状态,因为当前状态是pending,所以不会进行任何操作
console.log(resolved)
}, function (rejected) {
console.log(rejected)
})
结果就是控制台没有输出
但是如果是在ES6下,输出的结果是什么呢,我们可以看看是能够正常输出的
所以我们下面需要来解决一下这个异步的问题
异步任务 then 方法实现
我们现在的问题就是,如果promise里面是同步改变promise的状态,那么后面在进行then
方法的时候就能够拿到promise改变的状态;但是如果promise里面是异步改变的话,就需要等promise改变状态之后,才会执行then
的回调
那么怎么实现,等promise的状态发生改变之后,才会执行then
的回调呢?
其实也就是当我们在执行resolve
方法或者reject
方法的时候,我们需要去调用then
的回调。那很显然我们就需要保存then
的回调,不然当then
方法执行完之后,我们就没法再次获取到回调的内容。所以我们在then
方法中就需要把我们的回调保存起来。
所以我们需要在promise中新增一个属性,用来存储then
方法的回调,然后在then
方法执行的时候,如果当前的状态是pending
的话,就把回调保存起来,当以后状态从pending
变成其他状态的时候,就可以把回调方法拿出来执行了。
const PENDING = 'pending'
const FULFILLED = 'fulfilled'
const REJECTED = 'rejected'
function Promise(executor) { //构造函数传入一个方法,方法有两个参数,两个参数都是一个方法并且带一个传入参数
//添加属性
this.PromiseState = PENDING
this.PromiseResult = undefined
this.callback = {}; //保存then方法的回调
//保存实例对象的 this 的值
//此处可以不写,但是下面function方法需要改为箭头函数,否则function默认指向是window
const self = this
//resolve 函数
function resolve(data) {
if (self.PromiseState !== PENDING) { //判断状态,保证promise的状态只能从'pending'变为'fulfilled'
return;
}
//1. 修改对象的状态 (promiseState)
self.PromiseState = FULFILLED; // fulfilled
//2. 设置对象结果值 (promiseResult)
self.PromiseResult = data;
//调用成功的回调函数 加判断的原因是防止无回调报错
if (self.callback.onFulfilled) {
self.callback.onFulfilled(self.PromiseResult);
}
}
//reject 函数
function reject(data) {
//判断状态
if (self.PromiseState !== PENDING) { //判断状态,保证promise的状态只能从'pending'变为'rejected'
return;
}
//1. 修改对象的状态 (promiseState)
self.PromiseState = REJECTED; // rejected
//2. 设置对象结果值 (promiseResult)
self.PromiseResult = data;
//调用失败的回调函数 加判断的原因是防止无回调报错
if (self.callback.onRejected) {
self.callback.onRejected(self.PromiseResult);
}
}
try {
//同步调用『执行器函数』
executor(resolve, reject)
} catch (error) {
//修改 promise 对象状态为『失败』
reject(error)
}
}
Promise.prototype.then = function (onFulfilled, onRejected) {
// 当promise的状态为pending的时候就把回调方法保存起来,等待状态改变的时候进行调用
if (this.PromiseState === PENDING) {
this.callback = {
onFulfilled,
onRejected
}
}
//如果状态是成功或者失败的话,就直接执行回调方法
if (this.PromiseState === FULFILLED) {
onFulfilled(this.PromiseResult)
}
if (this.PromiseState === REJECTED) {
onRejected(this.PromiseResult)
}
}
然后我们在进行之前的测试,就可以发现能够正常输出了
根据Promise/A+规范 优化then方法
在规范中,对then
方法有下面这一段描述
- 2.2.1. Both onFulfilled and onRejected are optional arguments:
- 2.2.1.1. If onFulfilled is not a function, it must be ignored.
- 2.2.1.2. If onRejected is not a function, it must be ignored.
- 2.2.2. If onFulfilled is a function:
- 2.2.2.1. it must be called after promise is fulfilled, with promise’s value as its first argument.
- 2.2.2.2. it must not be called before promise is fulfilled.
- 2.2.2.3. it must not be called more than once.
- 2.2.3. If onRejected is a function,
- 2.2.3.1. it must be called after promise is rejected, with promise’s reason as its first argument.
- 2.2.3.2. it must not be called before promise is rejected.
- 2.2.3.3. it must not be called more than once.
上面的大部分我们已经实现了,但是还是有一些细节还没有实现,关于2.2.1
这部分规范我们需要优化一下
const PENDING = 'pending'
const FULFILLED = 'fulfilled'
const REJECTED = 'rejected'
function Promise(executor) { //构造函数传入一个方法,方法有两个参数,两个参数都是一个方法并且带一个传入参数
//添加属性
this.PromiseState = PENDING
this.PromiseResult = undefined
this.callback = {}; //保存then方法的回调
//保存实例对象的 this 的值
//此处可以不写,但是下面function方法需要改为箭头函数,否则function默认指向是window
const self = this
//resolve 函数
function resolve(data) {
if (self.PromiseState !== PENDING) { //判断状态,保证promise的状态只能从'pending'变为'fulfilled'
return;
}
//1. 修改对象的状态 (promiseState)
self.PromiseState = FULFILLED; // fulfilled
//2. 设置对象结果值 (promiseResult)
self.PromiseResult = data;
//调用成功的回调函数 加判断的原因是防止无回调报错
if (self.callback.onFulfilled) {
self.callback.onFulfilled(self.PromiseResult);
}
}
//reject 函数
function reject(data) {
//判断状态
if (self.PromiseState !== PENDING) { //判断状态,保证promise的状态只能从'pending'变为'rejected'
return;
}
//1. 修改对象的状态 (promiseState)
self.PromiseState = REJECTED; // rejected
//2. 设置对象结果值 (promiseResult)
self.PromiseResult = data;
//调用失败的回调函数 加判断的原因是防止无回调报错
if (self.callback.onRejected) {
self.callback.onRejected(self.PromiseResult);
}
}
try {
//同步调用『执行器函数』
executor(resolve, reject)
} catch (error) {
//修改 promise 对象状态为『失败』
reject(error)
}
}
Promise.prototype.then = function (onFulfilled, onRejected) {
// 根据标准,如果then的参数不是function,则我们需要忽略它,此处以如下方式处理
onFulfilled = typeof onFulfilled === 'function' ? onFulfilled : function (v) { }
onRejected = typeof onRejected === 'function' ? onRejected : function (r) { }
// 当promise的状态为pending的时候就把回调方法保存起来,等待状态改变的时候进行调用
if (this.PromiseState === PENDING) {
this.callback = {
onFulfilled,
onRejected
}
}
//如果状态是成功或者失败的话,就直接执行回调方法
if (this.PromiseState === FULFILLED) {
onFulfilled(this.PromiseResult)
}
if (this.PromiseState === REJECTED) {
onRejected(this.PromiseResult)
}
}
指定多个回调
我们可以从Promise/A+ 规范中可以看到同一个promise是可以指定多个then
的回调的
- 2.2.6. then may be called multiple times on the same promise.
- 2.2.6.1. If/when promise is fulfilled, all respective onFulfilled callbacks must execute in the order of their originating calls to then.
- 2.2.6.2. If/when promise is rejected, all respective onRejected callbacks must execute in the order of their originating calls to then.
比如在ES6的Promise中如下:
然而我们现在的代码是不支持的,因为我们现在保存的回调只有一个,所以后面执行then
方法保存的回调就会覆盖前面保存的回调。
所以显而易见,我们需要把我们的promise里面保存回调的属性,改成一个列表,我们需要存储一个回调的列表,等promise的状态发生变化的时候,就把这个回调列表都拿出来执行。
const PENDING = 'pending'
const FULFILLED = 'fulfilled'
const REJECTED = 'rejected'
function Promise(executor) { //构造函数传入一个方法,方法有两个参数,两个参数都是一个方法并且带一个传入参数
//添加属性
this.PromiseState = PENDING
this.PromiseResult = undefined
this.callbacks = [] //保存then方法的回调,这里保存的可能是多个回调,所以需要用列表来保存
//保存实例对象的 this 的值
//此处可以不写,但是下面function方法需要改为箭头函数,否则function默认指向是window
const self = this
//resolve 函数
function resolve(data) {
if (self.PromiseState !== PENDING) { //判断状态,保证promise的状态只能从'pending'变为'fulfilled'
return
}
//1. 修改对象的状态 (promiseState)
self.PromiseState = FULFILLED // fulfilled
//2. 设置对象结果值 (promiseResult)
self.PromiseResult = data
//这里需要把回调列表都循环调用一次
self.callbacks.forEach(callback => {
//调用成功的回调函数 加判断的原因是防止无回调报错
if (callback.onFulfilled) {
callback.onFulfilled(self.PromiseResult)
}
})
}
//reject 函数
function reject(data) {
//判断状态
if (self.PromiseState !== PENDING) { //判断状态,保证promise的状态只能从'pending'变为'rejected'
return
}
//1. 修改对象的状态 (promiseState)
self.PromiseState = REJECTED // rejected
//2. 设置对象结果值 (promiseResult)
self.PromiseResult = data
//这里需要把回调列表都循环调用一次
self.callbacks.forEach(callback => {
//调用失败的回调函数 加判断的原因是防止无回调报错
if (callback.onRejected) {
callback.onRejected(self.PromiseResult)
}
})
}
try {
//同步调用『执行器函数』
executor(resolve, reject)
} catch (error) {
//修改 promise 对象状态为『失败』
reject(error)
}
}
Promise.prototype.then = function (onFulfilled, onRejected) {
// 根据标准,如果then的参数不是function,则我们需要忽略它,此处以如下方式处理
onFulfilled = typeof onFulfilled === 'function' ? onFulfilled : function (v) { }
onRejected = typeof onRejected === 'function' ? onRejected : function (r) { }
// 当promise的状态为pending的时候就把回调方法保存起来,等待状态改变的时候进行调用
if (this.PromiseState === PENDING) {
this.callbacks.push({
onFulfilled,
onRejected
})
}
//如果状态是成功或者失败的话,就直接执行回调方法
if (this.PromiseState === FULFILLED) {
onFulfilled(this.PromiseResult)
}
if (this.PromiseState === REJECTED) {
onRejected(this.PromiseResult)
}
}
好,接下来用我们自己写的promise试试看
then 返回结果是一个新的promise对象
规范里说到,then方法返回的是一个新的promise对象
- 2.2.7. then must return a promise.
- promise2 = promise1.then(onFulfilled, onRejected);
所以接下来我们需要对我们的代码进行改造,在then方法中,最后return new Promise
const PENDING = 'pending'
const FULFILLED = 'fulfilled'
const REJECTED = 'rejected'
function Promise(executor) { //构造函数传入一个方法,方法有两个参数,两个参数都是一个方法并且带一个传入参数
//添加属性
this.PromiseState = PENDING
this.PromiseResult = undefined
this.callbacks = [] //保存then方法的回调,这里保存的可能是多个回调,所以需要用列表来保存
//保存实例对象的 this 的值
//此处可以不写,但是下面function方法需要改为箭头函数,否则function默认指向是window
const self = this
//resolve 函数
function resolve(data) {
if (self.PromiseState !== PENDING) { //判断状态,保证promise的状态只能从'pending'变为'fulfilled'
return
}
//1. 修改对象的状态 (promiseState)
self.PromiseState = FULFILLED // fulfilled
//2. 设置对象结果值 (promiseResult)
self.PromiseResult = data
//这里需要把回调列表都循环调用一次
self.callbacks.forEach(callback => {
//调用成功的回调函数 加判断的原因是防止无回调报错
if (callback.onFulfilled) {
callback.onFulfilled(self.PromiseResult)
}
})
}
//reject 函数
function reject(data) {
//判断状态
if (self.PromiseState !== PENDING) { //判断状态,保证promise的状态只能从'pending'变为'rejected'
return
}
//1. 修改对象的状态 (promiseState)
self.PromiseState = REJECTED // rejected
//2. 设置对象结果值 (promiseResult)
self.PromiseResult = data
//这里需要把回调列表都循环调用一次
self.callbacks.forEach(callback => {
//调用失败的回调函数 加判断的原因是防止无回调报错
if (callback.onRejected) {
callback.onRejected(self.PromiseResult)
}
})
}
try {
//同步调用『执行器函数』
executor(resolve, reject)
} catch (error) {
//修改 promise 对象状态为『失败』
reject(error)
}
}
Promise.prototype.then = function (onFulfilled, onRejected) {
// 根据标准,如果then的参数不是function,则我们需要忽略它,此处以如下方式处理
onFulfilled = typeof onFulfilled === 'function' ? onFulfilled : function (v) { }
onRejected = typeof onRejected === 'function' ? onRejected : function (r) { }
return new Promise((resolve, reject) => { // then 返回的是一个新的promise对象 / 2.2.7. then must return a promise.
// 当promise的状态为pending的时候就把回调方法保存起来,等待状态改变的时候进行调用
if (this.PromiseState === PENDING) {
this.callbacks.push({
onFulfilled,
onRejected
})
}
//如果状态是成功或者失败的话,就直接执行回调方法
if (this.PromiseState === FULFILLED) {
onFulfilled(this.PromiseResult)
}
if (this.PromiseState === REJECTED) {
onRejected(this.PromiseResult)
}
})
}
接下来我们可以测试看看。
结果可以发现,虽然then方法返回的确实是一个新的promise对象,但是这个promise对象的状态是pending
,而且promise的值是undefined
,这是因为我们没有调用resolve
或者reject
方法去改变promise的状态。
所以下面我们需要处理then(onFulfilled, onRejected)
返回promise的状态和值的问题。
那我们来看看规范是怎么描述的
- 2.2.7.1. If either onFulfilled or onRejected returns a value x, run the Promise Resolution Procedure [[Resolve]](promise2, x).
- 2.2.7.2. If either onFulfilled or onRejected throws an exception e, promise2 must be rejected with e as the reason.
- 2.2.7.3. If onFulfilled is not a function and promise1 is fulfilled, promise2 must be fulfilled with the same value as promise1.
- 2.2.7.4. If onRejected is not a function and promise1 is rejected, promise2 must be rejected with the same reason as promise1.
先看后面的描述,大概意思就是,当then(onFulfilled, onRejected)
中,如果回调方法onFulfilled, onRejected
中如果抛出异常,那么返回的新的promise状态是rejectd
,并且promise的值就是抛出的异常error。
然后如果then(onFulfilled, onRejected)
中的onFulfilled, onRejected
都不是一个方法,那么返回的新promise的状态,跟调用then方法的promise对象是同一个状态和同一个值,我们可以看看ES6中promise的情况
最后我们再来看第一条规范,如果then(onFulfilled, onRejected)
中的回调方法onFulfilled, onRejected
是一个方法,并且会有一个返回值x
,那么接下来就会调用Promise Resolution Procedure [[Resolve]](promise2, x)
方法,传入当前新的promise对象和onFulfilled, onRejected
返回的值x
。 这个Promise Resolution Procedure [[Resolve]](promise2, x)
方法里面的具体逻辑我们后面再来处理。
还有一点需要值得注意的就是then
方法中的回调方法是异步执行的,我们现在then
方法中的回调是同步执行的,而且如果是同步在后续的开发中你就会发现会报一个错:ReferenceError: Cannot access 'promise2' before initialization
,所以这点还需要改造一下。
所以我们开始接下来的改造
const PENDING = 'pending'
const FULFILLED = 'fulfilled'
const REJECTED = 'rejected'
function Promise(executor) { //构造函数传入一个方法,方法有两个参数,两个参数都是一个方法并且带一个传入参数
//添加属性
this.PromiseState = PENDING
this.PromiseResult = undefined
this.callbacks = [] //保存then方法的回调,这里保存的可能是多个回调,所以需要用列表来保存
//保存实例对象的 this 的值
//此处可以不写,但是下面function方法需要改为箭头函数,否则function默认指向是window
const self = this
//resolve 函数
function resolve(data) {
if (self.PromiseState !== PENDING) { //判断状态,保证promise的状态只能从'pending'变为'fulfilled'
return
}
//1. 修改对象的状态 (promiseState)
self.PromiseState = FULFILLED // fulfilled
//2. 设置对象结果值 (promiseResult)
self.PromiseResult = data
//这里需要把回调列表都循环调用一次
self.callbacks.forEach(callback => {
//调用成功的回调函数 加判断的原因是防止无回调报错
if (callback.onFulfilled) {
callback.onFulfilled(self.PromiseResult)
}
})
}
//reject 函数
function reject(data) {
//判断状态
if (self.PromiseState !== PENDING) { //判断状态,保证promise的状态只能从'pending'变为'rejected'
return
}
//1. 修改对象的状态 (promiseState)
self.PromiseState = REJECTED // rejected
//2. 设置对象结果值 (promiseResult)
self.PromiseResult = data
//这里需要把回调列表都循环调用一次
self.callbacks.forEach(callback => {
//调用失败的回调函数 加判断的原因是防止无回调报错
if (callback.onRejected) {
callback.onRejected(self.PromiseResult)
}
})
}
try {
//同步调用『执行器函数』
executor(resolve, reject)
} catch (error) {
//修改 promise 对象状态为『失败』
reject(error)
}
}
Promise.prototype.then = function (onFulfilled, onRejected) {
// the Promise Resolution Procedure [[Resolve]](promise2, x)
function resolvePromise(promise2, x, resolve, reject) {
}
// 根据标准,如果then的参数不是function,则我们需要忽略它,此处以如下方式处理
//2.2.7.3. If onFulfilled is not a function and promise1 is fulfilled, promise2 must be fulfilled with the same value as promise1.
//2.2.7.4. If onRejected is not a function and promise1 is rejected, promise2 must be rejected with the same reason as promise1.
onFulfilled = typeof onFulfilled === 'function' ? onFulfilled : (value) => value
onRejected = typeof onRejected === 'function' ? onRejected : (reason) => {throw reason}
const promise2 = new Promise((resolve, reject) => { // then 返回的是一个新的promise对象 / 2.2.7. then must return a promise.
// 当promise的状态为pending的时候就把回调方法保存起来,等待状态改变的时候进行调用
if (this.PromiseState === PENDING) {
this.callbacks.push({
onFulfilled: function (value) {
setTimeout(() => { // then方法中的回调是异步执行的
try {
// 2.2.7.1. If either onFulfilled or onRejected returns a value x, run the Promise Resolution Procedure [[Resolve]](promise2, x).
resolvePromise(promise2, onFulfilled(value), resolve, reject)
} catch (error) {
// 2.2.7.2. If either onFulfilled or onRejected throws an exception e, promise2 must be rejected with e as the reason.
reject(error)
}
})
},
onRejected: function (reason) {
setTimeout(() => { // then方法中的回调是异步执行的
try {
// 2.2.7.1. If either onFulfilled or onRejected returns a value x, run the Promise Resolution Procedure [[Resolve]](promise2, x).
resolvePromise(promise2, onRejected(reason), resolve, reject)
} catch (error) {
//2.2.7.2. If either onFulfilled or onRejected throws an exception e, promise2 must be rejected with e as the reason.
reject(error)
}
})
}
})
}
//如果状态是成功或者失败的话,就直接执行回调方法
if (this.PromiseState === FULFILLED) {
setTimeout(() => { // then方法中的回调是异步执行的
try {
// 2.2.7.1. If either onFulfilled or onRejected returns a value x, run the Promise Resolution Procedure [[Resolve]](promise2, x).
resolvePromise(promise2, onFulfilled(this.PromiseResult), resolve, reject)
} catch (error) {
// 2.2.7.2. If either onFulfilled or onRejected throws an exception e, promise2 must be rejected with e as the reason.
reject(error)
}
})
}
if (this.PromiseState === REJECTED) {
setTimeout(() => { // then方法中的回调是异步执行的
try {
// 2.2.7.1. If either onFulfilled or onRejected returns a value x, run the Promise Resolution Procedure [[Resolve]](promise2, x).
resolvePromise(promise2, onRejected(this.PromiseResult), resolve, reject)
} catch (error) {
// 2.2.7.2. If either onFulfilled or onRejected throws an exception e, promise2 must be rejected with e as the reason.
reject(error)
}
})
}
})
return promise2
}
接下来我们就需要实现The Promise Resolution Procedure
方法中的具体逻辑了,也就是我们上面代码中的resolvePromise
方法
The Promise Resolution Procedure
我们来看规范中对The Promise Resolution Procedure
方法的定义
- 2.3.1 If promise and x refer to the same object, reject promise with a TypeError as the reason.
- 2.3.2 If x is a promise, adopt its state [3.4]:
- 2.3.2.1 If x is pending, promise must remain pending until x is fulfilled or rejected.
- 2.3.2.2 If/when x is fulfilled, fulfill promise with the same value.
- 2.3.2.3 If/when x is rejected, reject promise with the same reason.
- 2.3.3 Otherwise, if x is an object or function,
- 2.3.3.1 Let then be x.then. [3.5]
- 2.3.3.2 If retrieving the property x.then results in a thrown exception e, reject promise with e as the reason.
- 2.3.3.3 If then is a function, call it with x as this, first argument resolvePromise, and second argument rejectPromise, where:
- 2.3.3.3.1 If/when resolvePromise is called with a value y, run [[Resolve]](promise, y).
- 2.3.3.3.2 If/when rejectPromise is called with a reason r, reject promise with r.
- 2.3.3.3.3 If both resolvePromise and rejectPromise are called, or multiple calls to the same argument are made, the first call takes precedence, and any further calls are ignored.
- 2.3.3.3.4 If calling then throws an exception e,
- 2.3.3.3.4.1 If resolvePromise or rejectPromise have been called, ignore it.
- 2.3.3.3.4.2 Otherwise, reject promise with e as the reason.
- 2.3.3.4 If then is not a function, fulfill promise with x.
首先来看2.3.1
这个怎么理解呢,其实就是then方法新返回的promise对象,不能跟then方法返回的值是同一个对象。这里我们可以看看ES6中的promise
可以看到上面代码中p1.then
方法中回调方法返回的值,刚好就是这个then
方法返回的新promise对象,这个时候就会报一个TypeError
后面的规范可以总结为以下几点:
- 如果
then
方法的回调方法返回的x
是promise对象,那么then
方法返回的promise对象的状态跟x
是一样的,并且值也是一样的。如果x
的状态是pending
,那么新返回的promise对象就会等到x
的状态变成fulfilled
或者rejected
- 如果
then
方法的回调方法返回的x
是一个thenable对象。比如下面这种
let thenable = {
then: function(resolve, reject) {
resolve(42);
}
};
那么就会就立即执行thenable对象的then()
方法,如果thenable对象的then()
方法最后如果是调用了resolve
方法,那么最后返回新的promise就是fulfilled
,如果thenable对象的then()
方法最后如果是调用了reject
方法,那么最后返回新的promise就是rejected
。如果thenable对象的then()
方法执行过程中抛出异常,那么最后返回新的promise就是rejected
。 注意这里如果thenable对象的then()
方法一旦调用了resolve
方法或者reject
方法或者抛出了异常之后状态就不会变了,后续调用多次resolve
方法或者reject
方法都不会改变结果。
我们可以看看ES6中的promise
- 如果
then
方法的回调方法返回的x
对象,如果x
对象有then
属性,但是不是一个方法的话,返回的是一个fulfilled
的promise。比如ES6中的promise
- 如果
then
方法的回调方法返回的x
是不是一个对象或者不是一个方法,也是返回的是一个fulfilled
的promise,比如ES6中的promise
接下来我们就来实现上面的规范。
const PENDING = 'pending'
const FULFILLED = 'fulfilled'
const REJECTED = 'rejected'
function Promise(executor) { //构造函数传入一个方法,方法有两个参数,两个参数都是一个方法并且带一个传入参数
if (typeof executor !== 'function') {
throw new TypeError(`Promise resolver ${executor} is not a function`)
}
//添加属性
this.PromiseState = PENDING
this.PromiseResult = undefined
this.callbacks = [] //保存then方法的回调,这里保存的可能是多个回调,所以需要用列表来保存
//保存实例对象的 this 的值
//此处可以不写,但是下面function方法需要改为箭头函数,否则function默认指向是window
const self = this
//resolve 函数
function resolve(data) {
if (self.PromiseState !== PENDING) { //判断状态,保证promise的状态只能从'pending'变为'fulfilled'
return
}
//1. 修改对象的状态 (promiseState)
self.PromiseState = FULFILLED // fulfilled
//2. 设置对象结果值 (promiseResult)
self.PromiseResult = data
//这里需要把回调列表都循环调用一次
self.callbacks.forEach(callback => {
//调用成功的回调函数 加判断的原因是防止无回调报错
if (callback.onFulfilled) {
callback.onFulfilled(self.PromiseResult)
}
})
}
//reject 函数
function reject(data) {
//判断状态
if (self.PromiseState !== PENDING) { //判断状态,保证promise的状态只能从'pending'变为'rejected'
return
}
//1. 修改对象的状态 (promiseState)
self.PromiseState = REJECTED // rejected
//2. 设置对象结果值 (promiseResult)
self.PromiseResult = data
//这里需要把回调列表都循环调用一次
self.callbacks.forEach(callback => {
//调用失败的回调函数 加判断的原因是防止无回调报错
if (callback.onRejected) {
callback.onRejected(self.PromiseResult)
}
})
}
try {
//同步调用『执行器函数』
executor(resolve, reject)
} catch (error) {
//修改 promise 对象状态为『失败』
reject(error)
}
}
Promise.prototype.then = function (onFulfilled, onRejected) {
// the Promise Resolution Procedure [[Resolve]](promise2, x)
function resolvePromise(promise2, x, resolve, reject) {
// 2.3.1 If promise and x refer to the same object, reject promise with a TypeError as the reason.
if (promise2 === x) {
// 防止进入死循环
reject(new TypeError("Chaining cycle detected for promise"))
}
if (x instanceof Promise) {
// 2.3.2 If x is a promise
// 判断如果是promise的话,则采用他的最终结果
x.then(
value => {
resolvePromise(promise2, value, resolve, reject)
},
reason => {
reject(reason)
}
)
} else if (x !== null && (typeof x === 'object' || typeof x === 'function')) {
// 2.3.3 Otherwise, if x is an object or function
let called = false
try {
// 2.3.3.1 Let then be x.then
// If retrieving the property x.then results in a thrown exception e, reject promise with e as the reason
// const then = x.then 包裹在try...catch..中
const then = x.then
// If then is a function, call it with x as this, first argument resolvePromise, and second argument rejectPromis
if (typeof then === "function") {
// 2.3.3.3.1 If/when resolvePromise is called with a value y, run [[Resolve]](promise, y).
// 2.3.3.3.2 If/when rejectPromise is called with a reason r, reject promise with r.
// 2.3.3.3.3 If both resolvePromise and rejectPromise are called, or multiple calls to the same argument are made, the first call takes precedence, and any further calls are ignored.
// 如果then是一个函数,则用x调用它;第一个参数是 resolvePromise,第二个参数是 rejectPromise
// 如果同时调用 resolvePromise 和 rejectPromise,或者多次调用同一个参数,则第一个调用具有优先权,后续的调用将被忽略。(所以需要使用 called 进行控制)
then.call(x, (y) => {
if (called) {
return
}
called = true
resolvePromise(promise2, y, resolve, reject)
}, (reason) => {
if (called) {
return
}
called = true
reject(reason)
})
} else {
resolve(x)
}
} catch (error) {
// 2.3.3.3.4 If calling then throws an exception e,
// 2.3.3.3.4.1 If resolvePromise or rejectPromise have been called, ignore it.
// 2.3.3.3.4.2 Otherwise, reject promise with e as the reason.
if (called) {
return
}
called = true
reject(error)
}
} else {
resolve(x)
}
}
// 根据标准,如果then的参数不是function,则我们需要忽略它,此处以如下方式处理
//2.2.7.3. If onFulfilled is not a function and promise1 is fulfilled, promise2 must be fulfilled with the same value as promise1.
//2.2.7.4. If onRejected is not a function and promise1 is rejected, promise2 must be rejected with the same reason as promise1.
onFulfilled = typeof onFulfilled === 'function' ? onFulfilled : (value) => value
onRejected = typeof onRejected === 'function' ? onRejected : (reason) => { throw reason }
const promise2 = new Promise((resolve, reject) => { // then 返回的是一个新的promise对象 / 2.2.7. then must return a promise.
// 当promise的状态为pending的时候就把回调方法保存起来,等待状态改变的时候进行调用
if (this.PromiseState === PENDING) {
this.callbacks.push({
onFulfilled: function (value) {
setTimeout(() => {
try {
// 2.2.7.1. If either onFulfilled or onRejected returns a value x, run the Promise Resolution Procedure [[Resolve]](promise2, x).
resolvePromise(promise2, onFulfilled(value), resolve, reject)
} catch (error) {
// 2.2.7.2. If either onFulfilled or onRejected throws an exception e, promise2 must be rejected with e as the reason.
reject(error)
}
})
},
onRejected: (reason) => {
setTimeout(() => {
try {
// 2.2.7.1. If either onFulfilled or onRejected returns a value x, run the Promise Resolution Procedure [[Resolve]](promise2, x).
resolvePromise(promise2, onRejected(reason), resolve, reject)
} catch (error) {
//2.2.7.2. If either onFulfilled or onRejected throws an exception e, promise2 must be rejected with e as the reason.
reject(error)
}
})
}
})
}
//如果状态是成功或者失败的话,就直接执行回调方法
if (this.PromiseState === FULFILLED) {
setTimeout(() => { // then方法中的回调是异步执行的
try {
// 2.2.7.1. If either onFulfilled or onRejected returns a value x, run the Promise Resolution Procedure [[Resolve]](promise2, x).
resolvePromise(promise2, onFulfilled(this.PromiseResult), resolve, reject)
} catch (error) {
// 2.2.7.2. If either onFulfilled or onRejected throws an exception e, promise2 must be rejected with e as the reason.
reject(error)
}
})
}
if (this.PromiseState === REJECTED) {
setTimeout(() => { // then方法中的回调是异步执行的
try {
// 2.2.7.1. If either onFulfilled or onRejected returns a value x, run the Promise Resolution Procedure [[Resolve]](promise2, x).
resolvePromise(promise2, onRejected(this.PromiseResult), resolve, reject)
} catch (error) {
// 2.2.7.2. If either onFulfilled or onRejected throws an exception e, promise2 must be rejected with e as the reason.
reject(error)
}
})
}
})
return promise2
}
最后我们可以通过promises-aplus-tests
工具来进行测试
首先需要安装promises-aplus-tests
npm i promises-aplus-tests -D
然后还需要在你的代码最后加上下面这段代码
Promise.defer = Promise.deferred = function() {
let dfd = {}
dfd.promise = new Promise((resolve, reject) => {
dfd.resolve = resolve
dfd.reject = reject
})
return dfd
}
所以整个test-promise.js
文件的内容如下:
const PENDING = 'pending'
const FULFILLED = 'fulfilled'
const REJECTED = 'rejected'
function Promise(executor) { //构造函数传入一个方法,方法有两个参数,两个参数都是一个方法并且带一个传入参数
if (typeof executor !== 'function') {
throw new TypeError(`Promise resolver ${executor} is not a function`)
}
//添加属性
this.PromiseState = PENDING
this.PromiseResult = undefined
this.callbacks = [] //保存then方法的回调,这里保存的可能是多个回调,所以需要用列表来保存
//保存实例对象的 this 的值
//此处可以不写,但是下面function方法需要改为箭头函数,否则function默认指向是window
const self = this
//resolve 函数
function resolve(data) {
if (self.PromiseState !== PENDING) { //判断状态,保证promise的状态只能从'pending'变为'fulfilled'
return
}
//1. 修改对象的状态 (promiseState)
self.PromiseState = FULFILLED // fulfilled
//2. 设置对象结果值 (promiseResult)
self.PromiseResult = data
//这里需要把回调列表都循环调用一次
self.callbacks.forEach(callback => {
//调用成功的回调函数 加判断的原因是防止无回调报错
if (callback.onFulfilled) {
callback.onFulfilled(self.PromiseResult)
}
})
}
//reject 函数
function reject(data) {
//判断状态
if (self.PromiseState !== PENDING) { //判断状态,保证promise的状态只能从'pending'变为'rejected'
return
}
//1. 修改对象的状态 (promiseState)
self.PromiseState = REJECTED // rejected
//2. 设置对象结果值 (promiseResult)
self.PromiseResult = data
//这里需要把回调列表都循环调用一次
self.callbacks.forEach(callback => {
//调用失败的回调函数 加判断的原因是防止无回调报错
if (callback.onRejected) {
callback.onRejected(self.PromiseResult)
}
})
}
try {
//同步调用『执行器函数』
executor(resolve, reject)
} catch (error) {
//修改 promise 对象状态为『失败』
reject(error)
}
}
Promise.prototype.then = function (onFulfilled, onRejected) {
// the Promise Resolution Procedure [[Resolve]](promise2, x)
function resolvePromise(promise2, x, resolve, reject) {
// 2.3.1 If promise and x refer to the same object, reject promise with a TypeError as the reason.
if (promise2 === x) {
// 防止进入死循环
reject(new TypeError("Chaining cycle detected for promise"))
}
if (x instanceof Promise) {
// 2.3.2 If x is a promise
// 判断如果是promise的话,则采用他的最终结果
x.then(
value => {
resolvePromise(promise2, value, resolve, reject)
},
reason => {
reject(reason)
}
)
} else if (x !== null && (typeof x === 'object' || typeof x === 'function')) {
// 2.3.3 Otherwise, if x is an object or function
let called = false
try {
// 2.3.3.1 Let then be x.then
// If retrieving the property x.then results in a thrown exception e, reject promise with e as the reason
// const then = x.then 包裹在try...catch..中
const then = x.then
// If then is a function, call it with x as this, first argument resolvePromise, and second argument rejectPromis
if (typeof then === "function") {
// 2.3.3.3.1 If/when resolvePromise is called with a value y, run [[Resolve]](promise, y).
// 2.3.3.3.2 If/when rejectPromise is called with a reason r, reject promise with r.
// 2.3.3.3.3 If both resolvePromise and rejectPromise are called, or multiple calls to the same argument are made, the first call takes precedence, and any further calls are ignored.
// 如果then是一个函数,则用x调用它;第一个参数是 resolvePromise,第二个参数是 rejectPromise
// 如果同时调用 resolvePromise 和 rejectPromise,或者多次调用同一个参数,则第一个调用具有优先权,后续的调用将被忽略。(所以需要使用 called 进行控制)
then.call(x, (y) => {
if (called) {
return
}
called = true
resolvePromise(promise2, y, resolve, reject)
}, (reason) => {
if (called) {
return
}
called = true
reject(reason)
})
} else {
resolve(x)
}
} catch (error) {
// 2.3.3.3.4 If calling then throws an exception e,
// 2.3.3.3.4.1 If resolvePromise or rejectPromise have been called, ignore it.
// 2.3.3.3.4.2 Otherwise, reject promise with e as the reason.
if (called) {
return
}
called = true
reject(error)
}
} else {
resolve(x)
}
}
// 根据标准,如果then的参数不是function,则我们需要忽略它,此处以如下方式处理
//2.2.7.3. If onFulfilled is not a function and promise1 is fulfilled, promise2 must be fulfilled with the same value as promise1.
//2.2.7.4. If onRejected is not a function and promise1 is rejected, promise2 must be rejected with the same reason as promise1.
onFulfilled = typeof onFulfilled === 'function' ? onFulfilled : (value) => value
onRejected = typeof onRejected === 'function' ? onRejected : (reason) => { throw reason }
const promise2 = new Promise((resolve, reject) => { // then 返回的是一个新的promise对象 / 2.2.7. then must return a promise.
// 当promise的状态为pending的时候就把回调方法保存起来,等待状态改变的时候进行调用
if (this.PromiseState === PENDING) {
this.callbacks.push({
onFulfilled: function (value) {
setTimeout(() => {
try {
// 2.2.7.1. If either onFulfilled or onRejected returns a value x, run the Promise Resolution Procedure [[Resolve]](promise2, x).
resolvePromise(promise2, onFulfilled(value), resolve, reject)
} catch (error) {
// 2.2.7.2. If either onFulfilled or onRejected throws an exception e, promise2 must be rejected with e as the reason.
reject(error)
}
})
},
onRejected: (reason) => {
setTimeout(() => {
try {
// 2.2.7.1. If either onFulfilled or onRejected returns a value x, run the Promise Resolution Procedure [[Resolve]](promise2, x).
resolvePromise(promise2, onRejected(reason), resolve, reject)
} catch (error) {
//2.2.7.2. If either onFulfilled or onRejected throws an exception e, promise2 must be rejected with e as the reason.
reject(error)
}
})
}
})
}
//如果状态是成功或者失败的话,就直接执行回调方法
if (this.PromiseState === FULFILLED) {
setTimeout(() => { // then方法中的回调是异步执行的
try {
// 2.2.7.1. If either onFulfilled or onRejected returns a value x, run the Promise Resolution Procedure [[Resolve]](promise2, x).
resolvePromise(promise2, onFulfilled(this.PromiseResult), resolve, reject)
} catch (error) {
// 2.2.7.2. If either onFulfilled or onRejected throws an exception e, promise2 must be rejected with e as the reason.
reject(error)
}
})
}
if (this.PromiseState === REJECTED) {
setTimeout(() => { // then方法中的回调是异步执行的
try {
// 2.2.7.1. If either onFulfilled or onRejected returns a value x, run the Promise Resolution Procedure [[Resolve]](promise2, x).
resolvePromise(promise2, onRejected(this.PromiseResult), resolve, reject)
} catch (error) {
// 2.2.7.2. If either onFulfilled or onRejected throws an exception e, promise2 must be rejected with e as the reason.
reject(error)
}
})
}
})
return promise2
}
Promise.defer = Promise.deferred = function () {
let dfd = {}
dfd.promise = new Promise((resolve, reject) => {
dfd.resolve = resolve;
dfd.reject = reject;
});
return dfd;
}
module.exports = Promise
最后通过命令来运行这个文件
npx promises-aplus-tests test-promise.js
可以看到最后所有的测试结果都是通过的
Promise.prototype.catch
接下来我们来实现Promise的其他原型方法
下面实现的就是catch
方法,catch
方法有一个特点就是异常穿透,能够捕获catch
方法前面then
方法或者promise中的异常。
其实catch
方法,本质上也是then
方法,只是他只接受onRejected
的回调。
记得我们在前面的实现中,如果promise中发生异常或者then方法中发生异常,最后返回的都是一个rejected
的promise对象, 接下来调用后面的then
方法的onRejected
的回调,但是如果后面的then
方法没有写onRejected
的回调,这个时候我们在代码中会判断如果没有的话,我们就给他设置一个
//2.2.7.4. If onRejected is not a function and promise1 is rejected, promise2 must be rejected with the same reason as promise1.
onRejected = typeof onRejected === 'function' ? onRejected : (reason) => { throw reason }
这个时候调用的话,就会继续往外抛一个异常,异常就会冒泡到遇到catch
方法。这个时候我们的catch
方法其实就是相当于then
方法中给他指定了onRejected
的回调。
所以Promise.prototype.catch
的回调如下:
Promise.prototype.catch = function(onRejected) {
return this.then(undefined, onRejected)
}
测试结果:
Promise.prototype.finally
之前我们讲过finally本质上是then方法的特例。
特点就是:如果finally中的回调抛出错误,那么最后返回一个rejected
的新promise。其他情况则和旧promise的状态和值一样。
promise
.finally(() => {
// 语句
});
// 等同于
promise
.then(
result => {
// 语句
return result;
},
error => {
// 语句
throw error;
}
);
所以最终我们实现出来的Promise.prototype.finally
方法如下:
Promise.prototype.finally = function (callback) {
return this.then(
(data) => {
callback()
return data
},
(error) => {
callback()
throw error
}
)
}
来看测试结果
Promise.resolve
接下来我们来开始讲Promise的静态方法了
之前也讲过Promise.resolve
其实就是简写的new Promise
的方式
Promise.resolve('foo')
// 等价于
new Promise(resolve => resolve('foo'))
Promise.resolve()方法的参数分成四种情况
- 如果参数是 Promise 实例,那么
Promise.resolve
将不做任何修改、原封不动地返回这个实例。 - 参数是一个
thenable
对象,Promise.resolve()
方法会将这个对象转为 Promise 对象,然后就立即执行thenable
对象的then()
方法。 - 参数不是具有then()方法的对象,或根本就不是对象
如果参数是一个原始值,或者是一个不具有then()
方法的对象,则Promise.resolve()
方法返回一个新的 Promise 对象,状态为resolved
- 不带有任何参数,·Promise.resolve()`方法允许调用时不带参数,直接返回一个resolved状态的 Promise 对象。
这四点中后面三点都是跟new Promise
的行为是一致的,只有第一点是不一致的。
我们可以看看ES6中promise的结果
所以在实现上我们只需要对第一种做特殊处理
Promise.resolve = function (value) {
return value instanceof Promise
? value
: new Promise((resolve) => resolve(value))
}
测试结果
Promise.reject
Promise.reject(reason)
方法也会返回一个新的 Promise 实例,该实例的状态为rejected
const p = Promise.reject('出错了');
// 等同于
const p = new Promise((resolve, reject) => reject('出错了'))
我们来看具体实现
Promise.reject = function (reason) {
return new Promise((resolve, reject) => reject(reason))
}
测试结果:
Promise.race
Promise.race()
方法是将多个 Promise 实例,包装成一个新的 Promise 实例。Promise.race()
方法的参数,如果不是 Promise 实例,就会将参数转为 Promise 实例,再进一步处理。运行的结果是参数里谁最先改变prmoise的状态,返回的新的promise的状态和值就与最先改变prmoise的状态的promise一致。
在这里Promise.race()
方法的参数,如果不是 Promise 实例,就会将参数转为 Promise 实例,再进一步处理。这里我们可以用Promise.resolve
来进行处理
所以具体的实现如下
Promise.race = function (promises) {
//需要注意的是,如果Promise.race接收到的是一个空数组([]),则会一直pending
return new Promise((resolve, reject) => {
promises.forEach((promise) => {
Promise.resolve(promise).then(v => resolve(v), r => reject(r))
})
})
}
测试结果:
Promise.all
Promise.all()
方法用于将多个 Promise 实例,包装成一个新的 Promise 实例。Promise.all()
方法接受一个数组作为参数,参数中的实例如果不是promise对象,就会先Promise.resolve
方法,将参数转为Promise
实例,再进一步处理。
新返回的promise对象分为两种情况
- 如果参数中的实例的状态都是
fulfilled
,最终才会变成fulfilled
的状态,此时参数中所有实例的返回值构成数组,传递给promises的回调函数。 - 如果参数中的实例有一个是
rejected
,那最终就是rejected
的状态,并且此时第一个被reject的实例的返回值,会传递给promises的回调函数。
下面来看具体的实现:
Promise.all = function (promises) {
return new Promise((resolve, reject) => {
// 如果Promise.all接收到的是一个空数组([]),会立即resolve。
if (!promises.length) {
resolve([])
}
let result = []
let resolvedPro = 0
for (let index = 0, length = promises.length; index < length; index++) {
Promise.resolve(promises[index]).then(
(data) => {
// 注意,这里要用index赋值,而不是push。因为要保持返回值和接收到的promise的位置一致性。
result[index] = data //let的块级作用域
if (++resolvedPro === length) {
resolve(result)
}
},
(error) => {
reject(error)
}
)
}
})
}
测试结果:
参考
剖析Promise内部结构,一步一步实现一个完整的、能通过所有Test case的Promise类
https://github.com/then/promise
按照 Promise/A+ 手写Promise,通过promises-aplus-tests的全部872个测试用例
【尚硅谷Web前端Promise教程从入门到精通-哔哩哔哩】
https://github.com/shifengming/promise/blob/master/promise.js