实现一个符合PromiseA+规范的Promise类

302 阅读17分钟

想要实现一个Promise,首先要了解Promise的一些特性和用法以及PromiseA+规范,了解用法和规范后,按照规范一步一步实现一个Promise

Promise的用法和特点

  • promise有且仅有下列状态中的其中一种状态
    • 等待态pending
    • 成功态resolved
    • 失败态rejected
  • 只有promise当前状态是penging时,才能将当前状态改变为resolved或者是rejected,其他情况不存在状态改变
  • Promise构造函数的第一个参数为executor,执行new Promise的时候executor立即执行
const p = new Promise((resolve, reject) => {
	console.log('立即执行了')
})
console.log(111)

可以看到先打印了立即执行了,再打印111 在这里插入图片描述

  • 当executor中抛出错误时,promise状态就会转变为失败态 在这里插入图片描述
  • executor接收两个参数resolvereject(resolve和reject都是函数),当当前promise状态为pending的时候,调用resolve将promise状态改变为resolved,或者调用reject,将promise状态改变为rejected
  • 每个promise实例都提供了一个then方法,在then方法中接收两个参数onFulfilled, onRejected,分别是成功的回调函数和失败的回调函数,then方法是异步的,属于微任务
const p = new Promise((resolve, reject) => {
	console.log(111)
	resolve(222)
})
p.then(res => {
	console.log(res, 'res')
}, err => {
	console.log(err, 'err')
})
console.log(333)

结果如下: 在这里插入图片描述

  • promisethen可以链式调用多次,成功时会执行所有的成功回调,失败时会执行所有的失败回调

实现一个Promise

在由简单到复杂的使用中实现一步一步实现一个Promise

先实现一个满足下面代码运行的Promise

const p1 = new Promise((resolve, reject) => {
    resolve(2)
})

p1.then(res => {
    console.log(res, 'success');
}, err => {
    console.log(err, 'error');
})

// 输出 2 'success'

源码实现部分:

const PENDING = 'pending',
	RESOLVED= 'resolved',
	REJECTED = 'rejected'

class Promise {
	constructor(executor) {
		this.status = PENDING // 当前promise的状态
		this.value = undefined // 成功态对应的value
		this.reason = undefined // 失败态对应的reason
		/*
		* 只有当状态为pending的时候才能够转换状态
		*/
		const resolve = value => {
			if(this.status === PENDING) {
				this.status = RESOLVED
				this.value = value
			}
		}
		const reject = reason => {
			if(this.status === PENDING) {
				this.status = REJECTED
				this.reason = reason
			}
		}
		executor(resolve, reject)
	}
    /*
    * onFulfilled: 一定是在promise是resolved状态后调用,并且接受一个参数 value
    * onRejected:  一定在 promise是rejected状态后调用,并且接受一个参数 reason
    */
	then(onFulfilled, onRejected) {
		if(this.status === RESOLVED) {
			onFulfilled(this.value)
		}
		if(this.status === REJECTED) {
			onRejected(this.reason)
		}
	}
}

这样就可以实现上面的效果,也可以打印出2 success的效果

  • 当执行executor函数时,如果内部出错,就直接转换为rejected状态,所以可以在执行executor时加上try catch来捕获错误
try {
   executor(resolve, reject)
}catch(e) {
   reject(e)
}
  • 目前executor代码内的代码是同步的运行起来是没有任何问题的,但是如果里面是异步的呢,这样就有问题了,以现在实现的Promise为基础运行以下代码
const p1 = new Promise((resolve, reject) => {
    setTimeout(() => {
        resolve(2)
    }, 1000)
})

p1.then(res => {
    console.log(res, 'success');
}, err => {
    console.log(err, 'error');
})

运行代码,发现什么也没发生,

原因:

执行new Promise(executor)立即执行executor,遇到了setTimeout,而setTimeout是异步的,会先将setTimeout的回调放到异步队列中,所以resolve并没有执行,当前promise的状态还是pending,而promise的then方法是微任务,所以先执行then,而这时候promise的状态还是pending,所以onFulfilled/onRejected都没有执行,而当一秒后执再行resolve,这时then方法已经执行过了,便不会再执行了。

解决办法:

在执行then方法时,如果当前的状态是pending,就把注册的onFulfilledonRejected方法先保存起来,等到状态变为resolved时,再依次执行保存的方法

代码如下:

class Promise {
    constructor(executor) {
        this.status = PENDING // 当前promise的状态
        this.value = undefined // 成功态对应的value
        this.reason = undefined // 失败态对应的reason
        this.onResolvedCallbacks = [] // 存储then函数的成功回调数组
        this.onRejectedCallbacks = []  // 存储then函数的失败回调数组
        /*
        * 只有当状态为pending的时候才能够转换状态
        */
        const resolve = value => {
            if(this.status === PENDING) {
                this.status = RESOLVED
                this.value = value
                /*
                * 状态变为resolved之后,依次执行保存的成功回调
                * */
                this.onResolvedCallbacks.forEach(fn => fn())
            }
        }
        const reject = reason => {
            if(this.status === PENDING) {
                this.status = REJECTED
                this.reason = reason
                /*
                * 状态变为rejected之后,依次执行保存的失败回调
                * */
                this.onRejectedCallbacks.forEach(fn => fn())
            }
        }
        try {
            executor(resolve, reject)
        }catch(e) {
            reject(e)
        }
    }

    /*
    * onFulfilled: 一定是在promise是resolved状态后调用,并且接受一个参数 value
    * onRejected:  一定在 promise是rejected状态后调用,并且接受一个参数 reason
    * */
    then(onFulfilled, onRejected) {
        if(this.status === RESOLVED) {
            onFulfilled(this.value)
        }
        if(this.status === REJECTED) {
            onRejected(this.reason)
        }
        /*
        * 状态为pending时,将成功和失败回调存起来
        * */
        if(this.status === PENDING) {
            this.onResolvedCallbacks.push(() => {
                onFulfilled(this.value)
            })
            this.onRejectedCallbacks.push(() => {
                onRejected(this.reason)
            })
        }
    }
}

现在再运行上面的例子就能够正常打印了

Promise链式调用

特点:

  1. promisethen方法可以链式调用,then方法一定返回一个新的promise

  2. 如果当前then中的代码报错,则会传递给下一个then的失败回调

const p1 = new Promise((resolve, reject) => {
  resolve(111)  
})

p1
  .then(res => {
    console.log(a, 'p1 success')
  }, err => {
   console.log(err, 'p1 error')
  })
  .then(res => {
    console.log(res, 'p1 then success')
   }, err => {
    console.log(err, 'p1 then error')
  })
 /*
 * ReferenceError: a is not defined
 *   at <anonymous>:7:17 "p1 then error"
 */
  1. 如果当前then中有返回值

    3.1 返回值是promise,就把promise的结果传递给下一个then

    3.2 返回值是普通值(除了promise以外的),就将这个值作为参数传递给下一个then的成功回调

  • 如果当前then中没有返回值,默认返回undefined

const p1 = new Promise((resolve, reject) => {
  resolve(111)  
})

p1
  .then(res => {
    console.log('p1 success')
    return new Promise((resolve, reject) => { resolve('========')})
  }, err => {
   console.log(err, 'p1 error')
  })
  .then(res => {
    console.log(res, 'p1 then success')
   }, err => {
    console.log(err, 'p1 then error')
  })
  /*
  * p1 success
  * ======== p1 then success
  */
const p1 = new Promise((resolve, reject) => {
  resolve(111)  
})

p1
  .then(res => {
    console.log('p1 success')
    return '-----p1p1p1------'
  }, err => {
   console.log(err, 'p1 error')
  })
  .then(res => {
    console.log(res, 'p1 then success')
   }, err => {
    console.log(err, 'p1 then error')
  })
  /*
  * p1 success
  * -----p1p1p1------ p1 then success
  */
  • 当前then没有任何处理逻辑,则无论成功还是失败都传递给下一个then的成功回调/失败回调
const p1 = new Promise((resolve, reject) => {
  resolve(111)  
})

p1
  .then()
  .then(res => {
    console.log(res, 'p1 then success')
   }, err => {
    console.log(err, 'p1 then error')
  })
  
  /*
  * 111 "p1 then success"
  */
const p1 = new Promise((resolve, reject) => {
  reject(111)  
})

p1
  .then()
  .then(res => {
    console.log(res, 'p1 then success')
   }, err => {
    console.log(err, 'p1 then error')
  })
  /*
  * 111 "p1 then error"
  */
  1. 当前then如果走到了失败回调中,只要失败回调中不发生错误,下一个then还是会走成功回调
const p1 = new Promise((resolve, reject) => {
  reject(222)
})

p1.then(res => {
  console.log(res, '1success')
}, err => {
  console.log(err, '1 fail')
}).then(res => {
  console.log(res, '2 success')
}, err => {
  console.log(err, '2 fail')
})
/*
* 222 "1 fail"
* undefined "2 success"
*/

根据以上使用,去实现源代码

promise.then一定是返回一个新的promise,因为通过上面的源代码可以知道,一个promise的状态和value/reason只能改变一次,如果返回的还是当前的promise,那不管调用多少次then,获取的还是老的promise的状态和当前值,这样链式调用也就没有意义了

源代码如下:(除了3.1没有实现,其他都实现了)

class Promise {
    constructor(executor) {
        this.status = PENDING // 当前promise的状态
        this.value = undefined // 成功态对应的value
        this.reason = undefined // 失败态对应的reason
        this.onResolvedCallbacks = [] // 存储then函数的成功回调
        this.onRejectedCallbacks = []  // 存储then函数的失败回调
        /*
        * 只有当状态为pending的时候才能够转换状态
        */
        const resolve = value => {
            if(this.status === PENDING) {
                this.status = RESOLVED
                this.value = value
                /*
                * 状态变为resolved之后,依次执行保存的成功回调
                * */
                this.onResolvedCallbacks.forEach(fn => fn())
            }
        }
        const reject = reason => {
            if(this.status === PENDING) {
                this.status = REJECTED
                this.reason = reason
                /*
                * 状态变为rejected之后,依次执行保存的失败回调
                * */
                this.onRejectedCallbacks.forEach(fn => fn())
            }
        }
        try {
            executor(resolve, reject)
        }catch(e) {
            reject(e)
        }
    }

    /*
    * onFulfilled: 一定是在promise是resolved状态后调用,并且接受一个参数 value
    * onRejected:  一定在 promise是rejected状态后调用,并且接受一个参数 reason
    * */
    then(onFulfilled, onRejected) {
        /*
        * 兼容一下没有传onFulfilled/onRejected的情况
        * 注意一下:
        *  onRejected如果没有传,
        *  默认返回reason => { throw reason }而不是 reason => reason
        *  因为要将错误传递到下一个then的onRejected,所以要抛出错误,才能走到下一个then的onRejected
        *  不然就将错误当作value传递个下一个then的onFulfilled了,这是不对的
        * */
        onFulfilled = typeof onFulfilled === 'function' ? onFulfilled : value => value
        onRejected = typeof onRejected === 'function' ? onRejected : reason => { throw reason }
        let promise2
        /*
        * 我们需要在then方法中返回一个新的promise
        * 另外:
        * 1、要获取onFulfilled的返回值,传递到返回的promise中
        * 2、在执行onFulfilled函数时,可能会报错,所以要加上try catch捕获错误
        * */
        promise2 = new Promise((resolve, reject) => {
            if(this.status === RESOLVED) {
                try {
                    let x = onFulfilled(this.value)
                    resolve(x)
                }catch(e) {
                    reject(e)
                }
            }
            if(this.status === REJECTED) {
                try {
                    /*
                    * 注意:
                    * 这里是resolve(x)而不是reject(x)
                    * */
                    let x = onRejected(this.reason)
                    resolve(x)
                }catch(e) {
                    reject(e)
                }
            }
            /*
            * 状态为pending时,将成功和失败的回调存起来
            * */
            if(this.status === PENDING) {
                this.onResolvedCallbacks.push(() => {
                    try {
                        let x = onFulfilled(this.value)
                        resolve(x)
                    }catch(e) {
                        reject(e)
                    }
                })
                this.onRejectedCallbacks.push(() => {
                    try {
                        let x = onRejected(this.reason)
                        resolve(x)
                    }catch(e) {
                        reject(e)
                    }
                })
            }
        })
        return promise2
    }
}

处理onFulfilled/onRejected返回值是promise的情况

可以看到代码中有四处地方都有返回值,所以我们写一个通用方法resolvePromise来处理返回值

    /*
    * onFulfilled: 一定是在promise是resolved状态后调用,并且接受一个参数 value
    * onRejected:  一定在 promise是rejected状态后调用,并且接受一个参数 reason
    * */
    then(onFulfilled, onRejected) {
        /*
        * 兼容一下没有传onFulfilled/onRejected的情况
        * 注意一下:
        *  onRejected如果没有传,
        *  默认返回reason => { throw reason }而不是 reason => reason
        *  因为要将错误传递到下一个then的onRejected,所以要抛出错误,才能走到下一个then的onRejected
        *  不然就将错误当作value传递个下一个then的onFulfilled了,这是不对的
        * */
        onFulfilled = typeof onFulfilled === 'function' ? onFulfilled : value => value
        onRejected = typeof onRejected === 'function' ? onRejected : reason => { throw reason }
        let promise2
        /*
        * 我们需要在then方法中返回一个新的promise
        * 另外:
        * 1、要获取onFulfilled的返回值,传递到返回的promise中
        * 2、在执行onFulfilled函数时,可能会报错,所以要加上try catch捕获错误
        * */
        promise2 = new Promise((resolve, reject) => {
            if(this.status === RESOLVED) {
                try {
                    let x = onFulfilled(this.value)
                    resolvePromise(promise2, x, resolve, reject)
                }catch(e) {
                    reject(e)
                }
            }
            if(this.status === REJECTED) {
                try {
                    /*
                    * 注意:
                    * 这里是resolve(x)而不是reject(x)
                    * */
                    let x = onRejected(this.reason)
                    resolvePromise(promise2, x, resolve, reject)
                }catch(e) {
                    reject(e)
                }
            }
            /*
            * 状态为pending时,将成功和失败的回调存起来
            * */
            if(this.status === PENDING) {
                this.onResolvedCallbacks.push(() => {
                    try {
                        let x = onFulfilled(this.value)
                        resolvePromise(promise2, x, resolve, reject)
                    }catch(e) {
                        reject(e)
                    }
                })
                this.onRejectedCallbacks.push(() => {
                    try {
                        let x = onRejected(this.reason)
                        resolvePromise(promise2, x, resolve, reject)
                    }catch(e) {
                        reject(e)
                    }
                })
            }
        })
        return promise2
    }

现在的源代码中存在一定问题

测试代码:

const p1 = new Promise((resolve, reject) => {
   resolve(100)
})

p1.then(res => {
    console.log(res);
}, err => {
    console.log(err);
})

我们在resolvePromise中添加打印信息

/*resolvePromise*/
const resolvePromise = (promise2, x, resolve, reject) => {
    console.log(promise2, x, resolve, reject);
    resolve(x)
}
// undefined undefined [Function: resolve] [Function: reject]

我们发现promise2undefined,这是因为promise2new Promise(executor)执行完后得到的实例,而在new Promise(executor)的时候,executor已经立即执行了,我们在executor函数中获取promise2时还没有new完,所以拿到的是undefined

解决方法:加上setTimeout,产生异步,这样等到执行resolvePromise执行时这样就能拿到promise2了,其实PromiseA+规范也是这么要求的

修改后如下:

promise2 = new Promise((resolve, reject) => {
            if(this.status === RESOLVED) {
                setTimeout(() => {
                    try {
                        let x = onFulfilled(this.value)
                        resolvePromise(promise2, x, resolve, reject)
                    }catch(e) {
                        reject(e)
                    }
                }, 0)
            }
            if(this.status === REJECTED) {
                setTimeout(() => {
                    try {
                        /*
                        * 注意:
                        * 这里是resolve(x)而不是reject(x)
                        * */
                        let x = onRejected(this.reason)
                        resolvePromise(promise2, x, resolve, reject)
                    }catch(e) {
                        reject(e)
                    }
                }, 0)
            }
            /*
            * 状态为pending时,将成功和失败的回调存起来
            * */
            if(this.status === PENDING) {
                this.onResolvedCallbacks.push(() => {
                    setTimeout(() => {
                        try {
                            let x = onFulfilled(this.value)
                            resolvePromise(promise2, x, resolve, reject)
                        }catch(e) {
                            reject(e)
                        }
                    }, 0)
                })
                this.onRejectedCallbacks.push(() => {
                    setTimeout(() => {
                        try {
                            let x = onRejected(this.reason)
                            resolvePromise(promise2, x, resolve, reject)
                        }catch(e) {
                            reject(e)
                        }
                    }, 0)
                })
            }
        })

这样就解决了promise2undeinfed的问题下面可以安心编写resolvePromise方法了

resolvePromise

先看下PromiseA+规范

  • 如果返回的promise2x是指向同一个引用(循环引用),则抛出错误

  • 如果x是一个promise实例,则采用它的状态:

    • 如果xpending状态,那么保留它(递归执行这个promise处理程序),直到pending状态转为 fulfilledrejected状态
    • 如果或当x状态是fulfilled,resolve 它,并且传入和 promise 一样的值value
    • 如果或当x状态是rejected,reject 它,并且传入和 promise 一样的值reason
  • 如果 x 是个对象或函数类型

    • x.then赋值给then变量

    • 如果捕获(try,catch)到x.then抛出的错误的话,需要reject这个promise

    • 如果then是函数类型,那个用x调用它(将 then 的 this 指向 x),第一个参数传resolvePromise ,第二个参数传rejectPromise

      • 如果或当resolvePromise被调用并接受一个参数y时,执行[[Resolve]](promise, y)
      • 如果或当rejectPromise被调用并接受一个参数r时,执行reject(r)
      • 如果resolvePromiserejectPromise已经被调用或以相同的参数多次调用的话吗,优先第一次的调用,并且之后的调用全部被忽略(避免多次调用)
    • 如果then执行过程中抛出了异常,

      • 如果resolvePromiserejectPromise已经被调用,那么忽略异常
      • 否则,则reject这个异常
    • 如果then不是函数类型,直接resolvex(resolve(x))

  • 如果x即不是函数类型也不是对象类型,直接resolve x(resolve(x))

实现:

promise2x是指向同一个引用这个是什么场景呢,如下代码:

const p = new Promise((resolve, reject) => {
  resolve(1)
})

const p2 = p.then(res => {
  return p2
})

p2.then(res => {
  console.log(res)
}, err => {
  console.log(err)
}) 
// 报错TypeError: Chaining cycle detected for promise #<Promise>

如上代码, p2 = p.then(res => { return p2 }),成功回调中返回的是一个promise,那么p2的状态将会等待这个promise调用resolve/reject来决定p2.then走成功回调还是失败回调,但是此时返回的是p2自己,等于是自己在等自己,但是自己又没有调用resolve/reject所以状态一直没有改变,代码就死在这里了,造成了死循环

判断是否循环引用

const resolvePromise = (promise2, x, resolve, reject) => {
	if(promise2 === x) {
	    return reject(new TypeError('Chaining cycle detected for promise'))
    }
}

对x的类型进行判断


const resolvePromise = (promise2, x, resolve, reject) => {
	if(promise2 === x) {
	    return reject(new TypeError('Chaining cycle detected for promise'))
    }
    /*
    * 先判断x否是对象或者函数,如果不是,说明x是原始值,直接resolve掉,是的话再进一步判断
    * 获取x.then赋值给then,判断then是否是一个函数,如果不是说明x就是一个普通对象,也直接resolve掉
    * 在获取x.then的时候,有可能会报错,比如这样写
    * Object.defineProperty(x, 'then', {
    *	get() {
    *   	throw new Error('故意刁难你')
    *	}
	*  })
	* 这样在执行x.then的时候就会报错,所以要用try catch捕获错误
	* 如果then是一个函数,我们认为x是一个promise,然后执行then并把x作为this传进去
	* 如果不指定this,那么promise内部获取this.value/this.reason/this.status就获取不到
	* 执行的then其实和平常使用.then一样 p.then(res => {}, err => {}),只不过使用call指定了this而已
    */
    if((typeof x === 'object' || typeof x === 'function') && x !== null) {
        try {
            const then = x.then
            if(typeof then === 'function') {
                then.call(x, y => {
                    // y就是传递给onFulfilled函数的参数
                    resolve(y)
                }, r => {
                	// r就是传递给onRejected函数的参数
                    reject(r)
                })
            }else {
                resolve(x)
            }
        }catch (e) {
            reject(e)
		}
    }else {
        resolve(x)
    }
}

上面在then函数成功回调的时候,直接resolve(y),有一些问题

比如:

const p = new Promise((resolve, reject) => {
  resolve(1)
})

const p2 = p.then(res => {
  return new Promise((resolve, reject) => {
    resolve(new Promise((resolve, reject) => {
      resolve(new Promise((resolve, reject) => {
        resolve(res)
      }))
    }))
  })
})

p2.then(res => {
  console.log(res) 
})
/*
* 原生的promise打印1
* 而现在自己写的打印的是一个Promise对象
* 在这里我们返回了一个promise实例,其中resolve了一个新的promise实例,调用then的时候,
* 接收的参数y是这个新的promise实例,直接resolve的话就不太对了,在这里我们做一些修改
*/

修改后的代码

then.call(x, y => {
    /*
    * 这里相当于做一个递归调用
    * 如果then方法成功回调接收的是promise,就再次调用resolvePromise方法
    * 直到获取到的值是普通值,在将这个值resolve掉
	*/
	resolvePromise(promise2, y, resolve, reject)
   }, r => {
   	// r就是传递给onRejected函数的参数
       reject(r)
   })

还有一个问题,resolvePromise,这个函数是所有promise通用的,但是别人的promise,有可能既会调resolve也会调reject,那么就会出问题了,所以我们接下来要做一下限制

	let called;
	if ((typeof x === 'object' || typeof x === 'function') && x !== null) {
		try {
			const then = x.then
			if (typeof then === 'function') {
				/*
				* 别人的promise可能在执行resolve后又执行reject,
				* 所以这里用一个变量来限制,保证成功/失败回调只会调用一次
				*/
				then.call(x, y => {
					if(called) return
					called = true
					resolvePromise(promise2, y, resolve, reject)
				}, r => {
					if(called) return
					called = true
					reject(r)
				})
			} else {
				resolve(x)
			}
		} catch (e) {
			if(called) return
			called = true
			reject(e)
		}
	} else {
		resolve(x)
	}

到这里差不多就写完了,但是还有一点小问题,比如运行如下代码:

const p1 = new Promise((resolve, reject) => {
    resolve(new Promise((resolve, reject) => {
        resolve(new Promise((resolve, reject) => {
            resolve(2)
        }))
    }))
})
p1.then(res => {
    console.log(res, 'res')
}, err => {
    console.log(err, 'err')
})

结果:

原生Promise打印的是2,我们自己写的打印的是Promise实例对象,这是因为resolve一个promise实例,在代码的resolve方法中,我们直接把这个值赋给了value,所以打印出来的不是promise执行后的结果,所以在resolve方法中需要做个判断,如果resolve函数传入的值是promise就直接执行这个promisethen方法

const resolve = value => {
	if(value instanceof Promise) {
		return value.then(resolve, reject)
	}
	if (this.status === PENDING) {
		this.status = RESOLVED
		this.value = value
		/*
		* 状态变为resolved之后,依次执行保存的成功回调
		* */
		this.onResolvedCallbacks.forEach(fn => fn())
	}
}

最后完整代码如下:

const PENDING = 'pending',
	RESOLVED = 'resolved',
	REJECTED = 'rejected'

const resolvePromise = (promise2, x, resolve, reject) => {
	if (promise2 === x) {
		return reject(new TypeError('Chaining cycle detected for promise'))
	}
	/*
	* 先判断x否是对象或者函数,如果不是,说明x是原始值,直接resolve掉,是的话再进一步判断
	* 获取x.then赋值给then,判断then是否是一个函数,如果不是说明x就是一个普通对象,也直接resolve掉
	* 在获取x.then的时候,有可能会报错,比如这样写
	* Object.defineProperty(x, 'then', {
	*	get() {
	*   	throw new Error('故意刁难你')
	*	}
	*  })
	* 这样在执行x.then的时候就会报错,所以要用try catch捕获错误
	* 如果then是一个函数,我们认为x是一个promise,然后执行then并把x作为this传进去
	* 如果不指定this,那么promise内部获取this.value/this.reason/this.status就获取不到
	* 执行的then其实和平常使用.then一样 p.then(res => {}, err => {}),只不过使用call指定了this而已
	*/
	let called;
	if ((typeof x === 'object' || typeof x === 'function') && x !== null) {
		try {
			const then = x.then
			if (typeof then === 'function') {
				then.call(x, y => {
					if(called) return
					called = true
					resolvePromise(promise2, y, resolve, reject)
				}, r => {
					if(called) return
					called = true
					reject(r)
				})
			} else {
				resolve(x)
			}
		} catch (e) {
			if(called) return
			called = true
			reject(e)
		}
	} else {
		resolve(x)
	}
}

class Promise {
	constructor(executor) {
		this.status = PENDING // 当前promise的状态
		this.value = undefined // 成功态对应的value
		this.reason = undefined // 失败态对应的reason
		this.onResolvedCallbacks = [] // 存储then函数的成功回调
		this.onRejectedCallbacks = []  // 存储then函数的失败回调
		/*
		* 只有当状态为pending的时候才能够转换状态
		*/
		const resolve = value => {
			if(value instanceof Promise) {
		        return value.then(resolve, reject)
	        }
			if (this.status === PENDING) {
				this.status = RESOLVED
				this.value = value
				/*
				* 状态变为resolved之后,依次执行保存的成功回调
				* */
				this.onResolvedCallbacks.forEach(fn => fn())
			}
		}
		const reject = reason => {
			if (this.status === PENDING) {
				this.status = REJECTED
				this.reason = reason
				/*
				* 状态变为rejected之后,依次执行保存的失败回调
				* */
				this.onRejectedCallbacks.forEach(fn => fn())
			}
		}
		try {
			executor(resolve, reject)
		} catch (e) {
			reject(e)
		}
	}

	/*
	* onFulfilled: 一定是在promise是resolved状态后调用,并且接受一个参数 value
	* onRejected:  一定在 promise是rejected状态后调用,并且接受一个参数 reason
	* */
	then(onFulfilled, onRejected) {
		/*
		* 兼容一下没有传onFulfilled/onRejected的情况
		* 注意一下:
		*  onRejected如果没有传,
		*  默认返回reason => { throw reason }而不是 reason => reason
		*  因为要将错误传递到下一个then的onRejected,所以要抛出错误,才能走到下一个then的onRejected
		*  不然就将错误当作value传递个下一个then的onFulfilled了,这是不对的
		* */
		onFulfilled = typeof onFulfilled === 'function' ? onFulfilled : value => value
		onRejected = typeof onRejected === 'function' ? onRejected : reason => {
			throw reason
		}
		let promise2
		/*
		* 我们需要在then方法中返回一个新的promise
		* 另外:
		* 1、要获取onFulfilled的返回值,传递到返回的promise中
		* 2、在执行onFulfilled函数时,可能会报错,所以要加上try catch捕获错误
		* */
		promise2 = new Promise((resolve, reject) => {
			if (this.status === RESOLVED) {
				setTimeout(() => {
					try {
						let x = onFulfilled(this.value)
						resolvePromise(promise2, x, resolve, reject)
					} catch (e) {
						reject(e)
					}
				}, 0)
			}
			if (this.status === REJECTED) {
				setTimeout(() => {
					try {
						/*
						* 注意:
						* 这里是resolve(x)而不是reject(x)
						* */
						let x = onRejected(this.reason)
						resolvePromise(promise2, x, resolve, reject)
					} catch (e) {
						reject(e)
					}
				}, 0)
			}
			/*
			* 状态为pending时,将成功和失败的回调存起来
			* */
			if (this.status === PENDING) {
				this.onResolvedCallbacks.push(() => {
					setTimeout(() => {
						try {
							let x = onFulfilled(this.value)
							resolvePromise(promise2, x, resolve, reject)
						} catch (e) {
							reject(e)
						}
					}, 0)
				})
				this.onRejectedCallbacks.push(() => {
					setTimeout(() => {
						try {
							let x = onRejected(this.reason)
							resolvePromise(promise2, x, resolve, reject)
						} catch (e) {
							reject(e)
						}
					}, 0)
				})
			}
		})
		return promise2
	}
}

module.exports = Promise

测试

在导出前加一段测试代码

Promise.defer = Promise.deferred = function(){
	let dfd = {};
	dfd.promise = new Promise((resolve,reject)=>{
		dfd.resolve = resolve;
		dfd.reject = reject;
	})
	return dfd;
}

module.exports = Promise

通过promises-aplus-tests可以测试我们实现的Promise类是否满足Promise/A+规范

npx promises-aplus-tests Promise.js

在这里插入图片描述

以上结果就代表通过测试,至此就完整实现了一个符合PromiseA+规范的Promise

参考

PromiseA+英文版 PromiseA+中文版