实现一个Promise
Promise是异步请求的一种解决方案,不同于传统的回调函数,它通过链式调用很好的解决了回调地狱,让异步执行清晰明了。在实际开发中也经常使用Promise,那Promise内部是如何实现的呢?
我们通过Promise的使用方式为突破口,实现一个Promise,为了与Promise区分,我们实现的Promise称它为MyPromise。
// Promise使用
let promise = new Promise((resolve, reject) => {
// TODO
// fulfilled => resolve(value)
// rejected => reject(reason)
})
上面是创建Promise实例最常用的写法,通过使用Promise,我们知道它有3个状态:pending、fulfilled、rejected,且状态只能由pending转变为fulfilled或pending转变为rejected,转变之后不能再变化,如:pending转变成fulfilled之后不能再变成rejected,也就是Promise状态变化不可逆。
我们通过new关键词生成一个Promise实例,可以看出Promise会接收一个函数(我们暂时称它为executor函数)作为参数,Promise实例生成后executor函数会被调用,也就是该函数内的代码被执行,executor函数接收resolve和reject两个参数,当Promise状态从pending转变成fulfilled时,会调用resolve函数,并传入状态变为fulfilled后的返回值value作为参数;当状态变成rejected时,调用reject函数,并传入状态变为rejected后的返回值reason作为参数。
通过上述分析,我们可以将MyPromise写成如下:
// MyPromise的三种状态
let PENDING = 'pending'
let FULFILLED = 'fulfilled'
let REJECTED = 'rejected'
function MyPromise(executor) {
let that = this
that.status = PENDING // 初始状态
that.value = undefined // 状态变成fulfilled后resolve的值
that.reason = undefined // 状态变成rejected后reject的原因
// 定义executor函数的参数resolve
function resolve(value) {
// TODO
if(that.status === PENDING){
that.status = FULFILLED
that.value = value
}
}
// 定义executor函数的参数reject
function reject(reason) {
// TODO
if(that.status === PENDING){
that.status = REJECTED
that.reason = reason
}
}
// 执行executor函数
try{
executor(resolve, reject)
} catch(e) {
reject(e)
}
}
上面我们搭建了MyPromise的基本框架,继续分析Promise的使用来完善MyPromise
// Promise使用
// 状态为fulfilled的Promise实例
let promise = new Promise((resolve, reject) => {
resolve(1)
})
// Promise实例具有then方法
let promise1 = promise.then((res) => {
console.log(res)
return res
}, (e) => {
console.log(e)
})
// 1
// Promise实例调用then方法后也返回一个Promise实例,且与之前的实例不同
promise1.then((res) => {
console.log(res)
})
// 1
promise === promise1
// false
// Promise实例then方法具有值穿透的功能
promise.then().then((res) => {
console.log(res)
})
// 1
// 状态为rejected的Promise实例
let promise2 = new Promise((resolve, reject) => {
reject(2)
})
let promise3 = promise2.then((res) => {
console.log(res)
}, (e) => {
console.log(e)
})
// 2
上面可以看出:1、Promise实例调用then方法后返回一个新的Promise实例,与之前的实例不是同一个东西;2、当then方法中参数为空时,then方法具有值穿透的功能;3、Promise实例都有then方法,then方法可以接收两个参数(我们暂时称它们分别为onFulfilled、onRejected),当Promise实例状态为fulfilled时,调用其then方法会执行onFulfilled函数,状态为rejected时,调用onRejected函数,当Promise实例状态为pending时,会将onFulfilled和onRejected函数分别添加到onFulfilledCallback和onRejectedCallback数组中,等待Promise实例状态转变,然后执行其对应的回调;
functon MyPromise(executor) {
...
that.onFulfilledCallback = [] // 存放当前状态为pending,状态如果转变为fulfilled时执行的回调onFulfilled
that.onRejectedCallback = [] // 存放当前状态为pending,状态如果转变为rejected时执行的回调onRejected
function resolve(value) {
if(that.status === PENDING){
...
that.onFulfilledCallback.forEach(cb => cb(that.value)) // 状态从pending转变为fulfilled时,执行其对应的onFulfilled回调
}
}
function reject(reason) {
if(that.status === PENDING){
...
that.onRejectedCallback.forEach(cb => cb(that.reason)) // 状态从pending转变为rejected时,执行其对应的onRejected回调
}
}
...
}
MyPromise.prototype.then = function (onFulfilled, onRejected){
let that = this
let promise1 // then方法要返回的新的实例
onFulfilled = typeof onFulfilled === 'function' ? onFulfilled : value => value // 当onFulfilled为空或者不为函数时让其具有值穿透功能
onRejected = typeof onRejected === 'function' ? onRejected : reason => { throw reason }
// 当状态为fulfilled时,执行onFulfilled方法
if(that.status === FULFILLED){
// 返回一个新的MyPromise实例,并根据onFulfilled回调执行结果将结果resolve或reject出来
return promise1 = new MyPromise((resolve, reject) => {
// 状态为fulfilled时,执行then方法中onFulfilled函数的结果
let x = onFulfilled(that.value)
// x的结果可能是一个promise、也可能是一个带有then方法的对象、也可能是一个普通对象、也可能是一个普通值,为了处理x值多种情况,通过resolvePromise方法对上述情况做统一处理
resolvePromise(promise1, x, resolve, reject)
})
}
// 当状态为rejected时,执行onRejected方法
if(that.status === REJECTED){
return promise1 = new MyPromise((resolve, reject) => {
// 状态为rejected时,执行then方法中onRejected函数的结果
let x = onRejected(that.reason)
resolvePromise(promise1, x, resolve, reject)
})
}
// 当状态为pending时,将onFulfilled和onRejected方法分别添加到onFulfilledCallback和onRejectedCallback数组中,等待状态变化时执行对应回调
if(that.status === PENDING){
return promise1 = new MyPromise((resolve, reject) => {
that.onFulfilledCallback.push((value) => {
let x = onFulfilled(value)
resolvePromise(promise1, x, resolve, reject)
})
that.onRejectedCallback.push((reason) => {
let x = onRejected(reason)
resolvePromise(promise1, x, resolve, reject)
})
})
}
}
function resolvePromise(promise, x, resolve, reject){
if(promise === x){
return reject(new TypeError('循环引用'))
}
let called = false // 当x为含有then方法的对象或函数时,防止x.then方法被多次调用
if(x instanceof MyPromise){ // x为MyPromise实例
if(x.status === PENDING){ // 当x状态为pending时,等待其状态转变为fulfilled时解析x返回值y或状态转变成rejected时执行reject回调
x.then(y => {
resolvePromise(promise, y, resolve, reject)
}, reason => {
reject(reason)
})
} else { // 当x状态为fulfilled或rejected时,通过then方法执行resolve或reject回调
x.then(resolve, reject)
}
} else if(x != null && (typeof x === 'object' || typeof x === 'function')){ // x为非空的对象或函数时
let then = x.then
if(typeof then === 'function'){ // x为含有then方法的对象或函数,等待x的执行结果,当x状态为fulfilled时,执行resolvePromise解析其返回值y,当x状态为rejected时,执行reject回调
then.call(x, y => {
if(called) return
called = true
resolvePromise(promise, y, resolve, reject)
}, reason => {
if(called) return
called = true
reject(reason)
})
} else { // x为普通的对象或函数
resolve(x)
}
} else { // x为普通值时,如:string、boolean、number等
resolve(x)
}
}
上述代码中完成了MyPromise的then方法,但是还有一个明显的问题没有解决,那就是MyPromise实例的执行顺序。
继续看如下代码:
// Promise使用
console.log('start')
let promise = new Promise((resolve, reject) => {
console.log(1)
resolve(1)
})
promise.then((res) => {
console.log(res + 1)
})
console.log('end')
// start
// 1
// end
// 2
从上述的执行结果可以看出,resolve之后,then方法中onFulfilled函数的执行是在当前事件循环结束后才执行,也就是onFulfilled的执行是异步的,为了保证MyPromise的执行顺序与Promise一致,需要在resolve、reject方法执行时通过setTimeout将其变为异步执行(这些是针对状态是pending的情况,对于状态已经是fulfilled或rejected的实例,为了保证then方法中的onFulfilled或onRejected异步执行,还需要在then方法中做处理),具体如下:
functon MyPromise(executor) {
...
function resolve(value) {
setTimeout(() => {
if(that.status === PENDING){
...
}
})
}
function reject(reason) {
setTimeout(() => {
if(that.status === PENDING){
...
}
})
}
...
}
MyPromise.prototype.then = function (onFulfilled, onRejected){
...
if(that.status === FULFILLED){
return promise1 = new MyPromise((resolve, reject) => {
setTimeout(() => {
let x = onFulfilled(that.value)
resolvePromise(promise1, x, resolve, reject)
})
})
}
if(that.status === REJECTED){
return promise1 = new MyPromise((resolve, reject) => {
setTimeout(() => {
let x = onRejected(that.reason)
resolvePromise(promise1, x, resolve, reject)
})
})
}
...
}
至此,MyPromise的实现基本就完成了,贴上完整的代码,如下:
// MyPromise的三种状态
let PENDING = 'pending'
let FULFILLED = 'fulfilled'
let REJECTED = 'rejected'
function MyPromise(executor) {
let that = this
that.status = PENDING // 初始状态
that.value = undefined // 状态变成fulfilled后resolve的值
that.reason = undefined // 状态变成rejected后reject的原因
that.onFulfilledCallback = [] // 存放当前状态为pending,状态如果转变为fulfilled时执行的回调onFulfilled
that.onRejectedCallback = [] // 存放当前状态为pending,状态如果转变为rejected时执行的回调onRejected
// 定义executor函数的参数resolve
function resolve(value) {
if(value instanceof MyPromise){
value.then(resolve, reject)
}
setTimeout(() => {
if(that.status === PENDING){
that.status = FULFILLED
that.value = value
that.onFulfilledCallback.forEach(cb => cb(that.value)) // 状态从pending转变为fulfilled时,执行其对应的onFulfilled回调
}
})
}
// 定义executor函数的参数reject
function reject(reason) {
setTimeout(() => {
if(that.status === PENDING){
that.status = REJECTED
that.reason = reason
that.onRejectedCallback.forEach(cb => cb(that.reason)) // 状态从pending转变为rejected时,执行其对应的onRejected回调
}
})
}
// 执行executor函数
try{
executor(resolve, reject)
} catch(e) {
reject(e)
}
}
MyPromise.prototype.then = function (onFulfilled, onRejected){
let that = this
let promise1 // then方法要返回的新的实例
onFulfilled = typeof onFulfilled === 'function' ? onFulfilled : value => value // 当onFulfilled为空或者不为函数时让其具有值穿透功能
onRejected = typeof onRejected === 'function' ? onRejected : reason => { throw reason }
// 当状态为fulfilled时,执行onFulfilled方法
if(that.status === FULFILLED){
// 返回一个新的MyPromise实例,并根据onFulfilled回调执行结果将结果resolve或reject出来
return promise1 = new MyPromise((resolve, reject) => {
setTimeout(() => {
try{
// 状态为fulfilled时,执行then方法中onFulfilled函数的结果
let x = onFulfilled(that.value)
// x的结果可能是一个promise、也可能是一个带有then方法的对象、也可能是一个普通对象、也可能是一个普通值,为了处理x值多种情况,通过resolvePromise方法对上述情况做统一处理
resolvePromise(promise1, x, resolve, reject)
} catch(e){
reject(e)
}
})
})
}
// 当状态为rejected时,执行onRejected方法
if(that.status === REJECTED){
return promise1 = new MyPromise((resolve, reject) => {
setTimeout(() => {
try{
// 状态为rejected时,执行then方法中onRejected函数的结果
let x = onRejected(that.reason)
resolvePromise(promise1, x, resolve, reject)
} catch(e){
reject(e)
}
})
})
}
// 当状态为pending时,将onFulfilled和onRejected方法分别添加到onFulfilledCallback和onRejectedCallback数组中,等待状态变化时执行对应回调
if(that.status === PENDING){
return promise1 = new MyPromise((resolve, reject) => {
that.onFulfilledCallback.push((value) => {
try{
let x = onFulfilled(value)
resolvePromise(promise1, x, resolve, reject)
} catch(e){
reject(e)
}
})
that.onRejectedCallback.push((reason) => {
try{
let x = onRejected(reason)
resolvePromise(promise1, x, resolve, reject)
} catch(e){
reject(e)
}
})
})
}
}
function resolvePromise(promise, x, resolve, reject){
if(promise === x){
return reject(new TypeError('循环引用'))
}
let called = false // 当x为含有then方法的对象或函数时,防止x.then方法被多次调用
if(x instanceof MyPromise){ // x为MyPromise实例
if(x.status === PENDING){ // 当x状态为pending时,等待其状态转变为fulfilled时解析x返回值y或状态转变成rejected时执行reject回调
x.then(y => {
resolvePromise(promise, y, resolve, reject)
}, reason => {
reject(reason)
})
} else { // 当x状态为fulfilled或rejected时,通过then方法执行resolve或reject回调
x.then(resolve, reject)
}
} else if(x != null && (typeof x === 'object' || typeof x === 'function')){ // x为非空的对象或函数时
try{
let then = x.then
if(typeof then === 'function'){ // x为含有then方法的对象或函数,等待x的执行结果,当x状态为fulfilled时,执行resolvePromise解析其返回值y,当x状态为rejected时,执行reject回调
then.call(x, y => {
if(called) return
called = true
resolvePromise(promise, y, resolve, reject)
}, reason => {
if(called) return
called = true
reject(reason)
})
} else { // x为普通的对象或函数
resolve(x)
}
} catch(e){
if(called) return
called = true
reject(e)
}
} else { // x为普通值时,如:string、boolean、number等
resolve(x)
}
}
最后通过promises-aplus-tests
来测试我们写的MyPromise,promises-aplus-tests
提供了很多测试用例,通过跑这些测试用例可以验证我们MyPromise是否正确,具体如下:
1、在MyPromise上添加一个延迟属性deferred,并将MyPromise导出
MyPromise.deferred = function () {
let defer = {}
defer.promise = new MyPromise((resolve, reject) => {
defer.resolve = resolve
defer.reject = reject
})
return defer
}
try{
module.exports = MyPromise
} catch(e){
throw new Error(e)
}
2、安装并执行promises-aplus-tests
npm i -g promises-aplus-tests
promises-aplus-tests MyPromise.js
测试用例全部通过
参考文献:
[1] Promise原理讲解 && 实现一个Promise对象 (遵循Promise/A+规范)
[2] Promise实现原理(附源码)
[3] 剖析Promise内部结构,一步一步实现一个完整的、能通过所有Test case的Promise类