Promise A+规范

160 阅读23分钟

1.什么是Promise?

  • Promise是一个有then方法的对象或函数
  • thenable是一个具有then方法的对象或函数
  • value是Promise成功状态的值,是resolve的参数,其值有undefined,Promise等
  • reason是Promise失败状态的值,是reject的参数
  • exception抛出异常

2.规范

Promise的状态

1. pending

  • 初始状态,可改变
  • 在resolve和reject执行之前都处于这个状态
  • 在resolve执行中,状态从pending变为fulfilled,并且不再改变
  • 在reject执行中,状态从pending变为rejected,并且不再改变

2. fulfilled

  • 成功的状态
  • resolve执行中会变为这个状态

3. rejected

  • 失败的状态
  • reject执行中会变为这个状态

小结:

  • pending - resolve() -> fulfilled
  • pending - reject() -> rejected
  • pending转换为fulfillef或rejected之后就不再改变,并且pending只能转换为fulfilled或rejected

resolve方法

  • 当前状态为pending时做以下操作
  • 改变状态把pending状态变为fulfilled
  • 改变当前的值,根据传递的参数作为当前的值
  • 执行then中的onFulfilled回调集合

reject方法

  • 当前状态为pending时做以下操作
  • 改变状态,把pending状态变为rejected
  • 改变当前值,根据传递的参数作为当前的值
  • 执行then中onRejected回调集合

Promise的then方法

  • 调用方式then(onFulfilled,onRejected)
  • 具有两个参数分别是onFulfilled和onRejected,并且是可选的
  • 返回一个的Promise对象

onFulfilled回调函数

  • 此方法必须是一个函数,如果不是一个函数则忽略掉(会默认创建一个新的函数)
  • 当状态从pending变为fulfilled之前,此方法不应该被调用
  • 当状态从pending变为fulfilled之后,此方法应该被调用,其参数为value
  • 此方法只能被调用一次

onRejected回调函数

  • 此方法必须是一个函数,如果不是一个函数则被忽略掉(会默认创建一个新的函数)
  • 当状态从pending变为rejected之前,此方法不应该被调用
  • 当状态从pending变为rejected之后,此方法应该被调用,其参数为reason
  • 此方法只能被调用

onFulfilled和onRejected回调函数是在微任务队列中执行

  • queueMicrotask(()=>{})可以当作微任务执行

then方法可以被同一个Promise调用多次

    const p = new Promise()
    p.then(fn1,err1)
    p.then(fn2,err2)
    p.then(fn3,err3)
  • 如果状态变为fulfilled那么onFulfilled(fn)会按照then顺序执行fn1->fn2->fn3
  • 如果状态变为rejected那么onRejected(err)会按照then顺序执行err1->err2->err3

then方法返回值

  • 返回一个新的Promise对象
const p = new Promise().then(fn1,err1)
// 等同写法
const p1 = new Promise()
const p = p1.then(fn1, err1)
p1 === p // false
  • 以上规范,就可以实现一个核心的Promise了,基本可以解决面试中的promise相关的题目

onFulfilled和onRejected执行结果处理

  • 如果onFulfilled和onRejected执行结果为x,那么把x放入resolvePromise中处理
  • 如果onFulfilled和onRejected执行结果其中有报错,那么直接通过then返回的新的Promise的reject执行
resolvePromise函数

resolvePromise(p,x,resolve,reject)

  • p: then中新的promise对象
  • x: onFulfilled和onRejected执行结果
  • resolve: then中新的promise对象的resolve
  • reject: then中新的promise对象的reject

如果p===x

  • 那么直接抛出异常说明p和x不能为同一个实例

如果x为Promise对象

  • x的fulfilled状态的结果value放在resolve中执行返回
  • x的rejected状态的结果reason放在reject中执行返回

如果x是一个函数或对象

  • let then = x.then
  • then如果是一个函数,then.call(this)
  • then如果是一个对象,resolve(then)

如果x是其他值

  • resolve(x)

Promise.resolve()方法

  • 直接通过构造器调用的方法属于静态方法,通过实例调用的方法属于实例方法
  • 接收一个参数作
  • 如果这个参数是Promise对象,那么直接返回这个值
  • 如果不是Promise对象,那么返回一个新的Promise对象,把参数作为成功状态的值

Promise.reject()方法

  • 接受一个参数
  • 直接返回一个新的Promise,并且把这个参数作为失败状态的值
  • 因此如果传递的是一个promise对象并且是失败的状态,那么直接得到的就是这个对象
const a = new Promise((res, rej) => { rej(22) })
Promise.reject(a).then(res => { 
    console.log(res)}, err => { 
    alert(err) // Promise对象,因为内部直接把参数作为失败的值返回
}) 
Promise.resolve(a).then(res => { 
    console.log(res)}, err => { 
    alert(err) // 22
})

Promise.all()方法

  • 参数必须是一个数组,如果不是一个数组直接报错提示
  • 数组中的值可以是Promise对象也可以不是
  • 数组中的每一项都会被Promise执行
  • 如果数组中有失败的状态,那么直接reject返回其值
  • 如果数组中没有失败的状态,那么成功的值一定是按照数组的顺序返回一个数组

catch(fn)方法

  • 接受一个函数作为参数
  • 其内部其实调用了then(null,fn)方法,并且返回then执行的结果
  • 因此catch返回的值也是一个新的Promise对象

根据规范代码实现

实现Promise的声明
// 实现的功能
const p = new Promise(() => {})

//实现的代码
// Promise是一个构造函数
class MPromise {
    constructor(fn){
    }
}
实现Promise的基本使用
// 实现的功能
const p = new Promise((resolve, reject) => {
    resolve('成功')
})
//实现的代码
// 定义三种状态
const PENDING = 'PENDING'
const FULFILLED = 'FULFILLED'
const REJECTED = 'REJECTED'
// Promise是一个构造函数
class MPromise {
    constructor(fn){
        // 当前状态下的值
        this.value = null
        // 当前状态 初始状态为pending
        this.status = PENDING
        // fn同步执行,把resolve和reject传递进去
        fn(this.resolve.bind(this), this.reject.bind(this))
    }
    // resolve的实现
    resolve (val) {
        // 只有在pending的时候才能改变状态和值
        if (this.status === PENDING) {
            // 改变状态
            this.status = FULFILLED
            // 改变值
            this.value = val
            // 执行回调集合,暂不处理,等到then完成之后再处理
        }
    }
    // reject的实现
    reject (reason) {
        // 只有在pending的时候才能改变状态和值
        if (this.status === PENDING) {
            this.status = REJECTED
            this.value = reason
            // 执行回调集合,暂不处理,等到then完成之后再处理
        }
    }
}
实现then方法
// 实现功能
const p = new Promise((resolve, reject) => {
    resolve('成功')
}).then(res => { 
    console.log(res) // 成功
}, err => {})
// 代码实现
// 定义三种状态
const PENDING = 'PENDING'
const FULFILLED = 'FULFILLED'
const REJECTED = 'REJECTED'
// Promise是一个构造函数
class MPromise {
    constructor(fn){
        // 当前状态下的值
        this.value = null
        // 当前状态 初始状态为pending
        this.status = PENDING
        // fn同步执行,把resolve和reject传递进去
        fn(this.resolve.bind(this), this.reject.bind(this))
    }
    // resolve的实现
    resolve (val) {
        // 只有在pending的时候才能改变状态和值
        if (this.status === PENDING) {
            // 改变状态
            this.status = FULFILLED
            // 改变值
            this.value = val
            // 执行回调集合,暂不处理,等到then完成之后再处理
        }
    }
    // reject的实现
    reject (reason) {
        // 只有在pending的时候才能改变状态和值
        if (this.status === PENDING) {
            this.status = REJECTED
            this.value = reason
            // 执行回调集合,暂不处理,等到then完成之后再处理
        }
    }
    // then方法
    then (onFulfilled, onRejected) {
        // 如果回调函数不是一个函数则忽略。初始一个函数
        onFulfilled = this.isFunction(onFulfilled) ? onFulfilled : (value) => { return value }
        onRejected = this.isFunction(onFulfilled) ? onRejected : (reason) => { throw reason }
        // then方法返回一个新的promise
        const p = new MPromise((resolve, reject) => {
            // 根据当前状态判断具体执行成功回调还是失败回调
            switch(this.status){
                case FULFILLED: { // 如果是成功状态就执行成功的回调并把当前值传递进去
                    onFulfilled(this.value)
                    break;
                }
                case REJECTED: { // 如果是失败的状态就执行失败的回调把当前值传递进去
                    onRejected(this.value)
                    break;
                }
            }
        })
        return p
    }
    // 判断是否是一个函数
    isFunction (fn) {
        return typeof fn === 'function'
    }
}

// 测试
const p = new MPromise((resolve, reject) => {
    resolve('成功')
}).then(res => { 
    console.log(res) // 成功 
}, err => {})
  • 以上就是实现了一个同步的Promise,然而Promise主要是解决异步的回调,在then的switch只判断了成功和失败的状态,而pending状态没有处理,什么时候会是pending状态,pending状态是在Promise初始化的时候开始直到调用了成功或失败的方法之前都是处于pending状态,而成功和失败的方法的执行往往实在拿到结果之后才会执行。因此这个pending状态的时间段就是异步的,可能处于网络请求状态,可能处于延时状态等,因此根据pending就可以实现异步的回调执行,在pending状态需要把对应的回调存储起来,等到resolve或reject执行的时候再执行
// 主要代码实现
class MPromise {
    constructor(fn){
        // 定义两个数组分别用来存储pending时候的回调函数
        // 注意这里为什么要用数组,根据规范的then下的第四点,是因为同一个Promise实例可以多次调用then
        this.fulfilledList = []
        this.rejectedList = []
        ...
    }
    // resolve的实现
    resolve (val) {
        // 只有在pending的时候才能改变状态和值
        if (this.status === PENDING) {
            ...
            // 执行回调集合
            this.fulfilledList.forEach(cb => {
                cb()
            })
        }
    }
    // reject的实现
    reject (reason) {
        // 只有在pending的时候才能改变状态和值
        if (this.status === PENDING) {
           ...
            // 执行回调集合
            this.rejectedList.forEach(cb => {
                cb()
            })
        }
    }
    // then方法
    then (onFulfilled, onRejected) {
         ...
        // then方法返回一个新的promise
        const p = new MPromise((resolve, reject) => {
            // 根据规范 成功和失败的回调是在微任务队列中执行
            const onFulfilledMicroTask = () => {
                queueMicrotask(() => {
                    onFulfilled(this.value)
                })
            }
            const onRejectedMicroTask = () => {
                queueMicrotask(() => {
                    onRejected(this.value)
                })
            }
            // 根据当前状态判断具体执行成功回调还是失败回调
            switch(this.status){
                ...
                case PENDING: { // 如果是pending状态就需要把对应的回调存储起来
                    this.fulfilledList.push(onFulfilledMicroTask)
                    this.rejectedList.push(onRejectedMicroTask)
                    break;
                }
            }
        })
        return p
    }
}
  • 完整代码
// 实现功能
const p = new Promise((resolve, reject) => {
    setTimeout(() => {
        resolve('成功')
    }, 2000)
}).then(res => { 
    console.log(res) // 成功
}, err => {})
// 代码实现
// 定义三种状态
const PENDING = 'PENDING'
const FULFILLED = 'FULFILLED'
const REJECTED = 'REJECTED'
// Promise是一个构造函数
class MPromise {
    constructor(fn){
        // 定义两个数组分别用来存储pending时候的回调函数
        // 注意这里为什么要用数组,根据规范的then下的第四点,是因为同一个Promise实例可以多次调用then
        this.fulfilledList = []
        this.rejectedList = []
        // 当前状态下的值
        this.value = null
        // 当前状态 初始状态为pending
        this.status = PENDING
        // fn同步执行,把resolve和reject传递进去
        fn(this.resolve.bind(this), this.reject.bind(this))
    }
    // resolve的实现
    resolve (val) {
        // 只有在pending的时候才能改变状态和值
        if (this.status === PENDING) {
            // 改变状态
            this.status = FULFILLED
            // 改变值
            this.value = val
            // 执行回调集合
            this.fulfilledList.forEach(cb => {
                cb()
            })
        }
    }
    // reject的实现
    reject (reason) {
        // 只有在pending的时候才能改变状态和值
        if (this.status === PENDING) {
            this.status = REJECTED
            this.value = reason
            // 执行回调集合
            this.rejectedList.forEach(cb => {
                cb()
            })
        }
    }
    // then方法
    then (onFulfilled, onRejected) {
        // 如果回调函数不是一个函数则忽略。初始一个函数
        onFulfilled = this.isFunction(onFulfilled) ? onFulfilled : (value) => { return value }
        onRejected = this.isFunction(onFulfilled) ? onRejected : (reason) => { throw reason }
        // then方法返回一个新的promise
        const p = new MPromise((resolve, reject) => {
             // 根据规成功和失败的回调是在微任务队列中执行
            const onFulfilledMicroTask = () => {
                queueMicrotask(() => {
                    onFulfilled(this.value)
                })
            }
            const onRejectedMicroTask = () => {
                queueMicrotask(() => {
                    onRejected(this.value)
                })
            }
            // 根据当前状态判断具体执行成功回调还是失败回调
            switch(this.status){
                case FULFILLED: { // 如果是成功状态就执行成功的回调并把当前值传递进去
                    onFulfilledMicroTask(this.value)
                    break;
                }
                case REJECTED: { // 如果是失败的状态就执行失败的回调把当前值传递进去
                    onRejectedMicroTask(this.value)
                    break;
                }
                case PENDING: { // 如果是pending状态就需要把对应的回调存储起来
                    this.fulfilledList.push(onFulfilledMicroTask)
                    this.rejectedList.push(onRejectedMicroTask)
                    break;
                }
            }
        })
        return p
    }
    // 判断是否是一个函数
    isFunction (fn) {
        return typeof fn === 'function'
    }
}

// 测试
const p = new MPromise((resolve, reject) => {
    setTimeout(() => {
        resolve('成功')
    }, 2000)
}).then(res => { 
    console.log(res) // 成功 
}, err => {})
  • 以上就基本实现了一个核心的Promise
  • then方法可以有返回值,比如我们在then中返回一个Promise,一个基本数值等,而这个值的处理就在resolvePromise函数中
// 实现功能
const p = new Promise((resolve, reject) => {
    setTimeout(() => {
        resolve('成功')
    }, 2000)
}).then(res => { 
    console.log(res) // 成功
    return new Promise(res => {
        res('成功2')
    })
}, err => {}).then(res => {
    console.log(res) // 成功2
})
// 代码实现
// Promise是一个构造函数
class MPromise {
    // then方法
    then (onFulfilled, onRejected) {
        // then方法返回一个新的promise
        const p = new MPromise((resolve, reject) => {
             // 根据规成功和失败的回调是在微任务队列中执行
            const onFulfilledMicroTask = () => {
                queueMicrotask(() => {
                    try{
                       const x = onFulfilled(this.value)
                       // 把返回的结果传递到resolvePromise
                       this.resolvePromise(p, x, resolve, reject)
                    }catch(e){
                        reject(e)
                    }
                })
            }
            const onRejectedMicroTask = () => {
                queueMicrotask(() => {
                   try{
                       const x = onRejected(this.value)
                       this.resolvePromise(p, x, resolve, reject)
                    }catch(e){
                        reject(e)
                    }
                })
            }
        })
        return p
    }
    // 在resolvePromise中对then中的回调结果进行处理
    resolvePromise (p, x, resolve, reject) {
        // 如果p和x相等需要抛错
        if (p === x) {
           return  throw 'p和x不能是同一个Promise对象'
        }
        // 如果x是一个Promise
        if (x instanceof MPromise) {
            x.then(res => {
                resolve(res)
            },err => {
                reject(err)
            })
        } else if (typeof x === 'function' || typeof x=== 'object') { // 如果是一个函数或对象
            // 如果是null 直接resolve
            if (x === null) {
               return resolve(null)
            }
            try {
                 const then = x.then
                // 根据规范 如果then是一个函数通过this调用,否则直接resolve返回
                if (typeof then === 'function') {
                    return then.call(this)
                } else {
                    return resolve(then)
                }
            } catch(e) {
                return reject(e)
            }
        } else { // 其他值
            return resolve(x)
        }
    }
}
  • 完整代码
// 实现功能
const p = new Promise((resolve, reject) => {
    setTimeout(() => {
        resolve('成功')
    }, 2000)
}).then(res => { 
    console.log(res) // 成功
    return new Promise(res => {
        res('成功2')
    })
}, err => {}).then(res => {
    console.log(res) // 成功2
})
// 代码实现
// 定义三种状态
const PENDING = 'PENDING'
const FULFILLED = 'FULFILLED'
const REJECTED = 'REJECTED'
// Promise是一个构造函数
class MPromise {
    constructor(fn){
        // 定义两个数组分别用来存储pending时候的回调函数
        // 注意这里为什么要用数组,根据规范的then下的第四点,是因为同一个Promise实例可以多次调用then
        this.fulfilledList = []
        this.rejectedList = []
        // 当前状态下的值
        this.value = null
        // 当前状态 初始状态为pending
        this.status = PENDING
        // fn同步执行,把resolve和reject传递进去
        fn(this.resolve.bind(this), this.reject.bind(this))
    }
    // resolve的实现
    resolve (val) {
        // 只有在pending的时候才能改变状态和值
        if (this.status === PENDING) {
            // 改变状态
            this.status = FULFILLED
            // 改变值
            this.value = val
            // 执行回调集合
            this.fulfilledList.forEach(cb => {
                cb()
            })
        }
    }
    // reject的实现
    reject (reason) {
        // 只有在pending的时候才能改变状态和值
        if (this.status === PENDING) {
            this.status = REJECTED
            this.value = reason
            // 执行回调集合
            this.rejectedList.forEach(cb => {
                cb()
            })
        }
    }
    // then方法
    then (onFulfilled, onRejected) {
        // 如果回调函数不是一个函数则忽略。初始一个函数
        onFulfilled = this.isFunction(onFulfilled) ? onFulfilled : (value) => { return value }
        onRejected = this.isFunction(onFulfilled) ? onRejected : (reason) => { throw reason }
        // then方法返回一个新的promise
        const p = new MPromise((resolve, reject) => {
             // 根据规成功和失败的回调是在微任务队列中执行
            const onFulfilledMicroTask = () => {
                queueMicrotask(() => {
                    try{ // try是因为promise中只要报错就走reject
                       const x = onFulfilled(this.value)
                       this.resolvePromise(p, x, resolve, reject)
                    }catch(e){
                        reject(e)
                    }
                })
            }
            const onRejectedMicroTask = () => {
                queueMicrotask(() => {
                   try{
                       const x = onRejected(this.value)
                       this.resolvePromise(p, x, resolve, reject)
                    }catch(e){
                        reject(e)
                    }
                })
            }
            // 根据当前状态判断具体执行成功回调还是失败回调
            switch(this.status){
                case FULFILLED: { // 如果是成功状态就执行成功的回调并把当前值传递进去
                    onFulfilledMicroTask(this.value)
                    break;
                }
                case REJECTED: { // 如果是失败的状态就执行失败的回调把当前值传递进去
                    onRejectedMicroTask(this.value)
                    break;
                }
                case PENDING: { // 如果是pending状态就需要把对应的回调存储起来
                    this.fulfilledList.push(onFulfilledMicroTask)
                    this.rejectedList.push(onRejectedMicroTask)
                    break;
                }
            }
        })
        return p
    }
    // 判断是否是一个函数
    isFunction (fn) {
        return typeof fn === 'function'
    }
    
    // 在resolvePromise中对then中的回调结果进行处理
    resolvePromise (p, x, resolve, reject) {
        // 如果p和x相等需要抛错
        if (p === x) {
           throw 'p和x不能是同一个Promise对象'
        }
        // 如果x是一个Promise
        if (x instanceof MPromise) {
            x.then(res => {
                resolve(res)
            },err => {
                reject(err)
            })
        } else if (typeof x === 'function' || typeof x=== 'object') { // 如果是一个函数或对象
            // 如果是null 直接resolve
            if (x === null) {
               return resolve(null)
            }
            try {
                 const then = x.then
                // 根据规范 如果then是一个函数通过this调用,否则直接resolve返回
                if (typeof then === 'function') {
                    return then.call(this)
                } else {
                    return resolve(then)
                }
            } catch(e) {
                return reject(e)
            }
        } else { // 其他值
            return resolve(x)
        }
    }
}

// 测试
const p = new MPromise((resolve, reject) => {
    setTimeout(() => {
        resolve('成功')
    }, 2000)
}).then(res => { 
    console.log(res) // 成功
    return new MPromise(res => {
        res('成功2')
    })
}, err => {}).then(res => {
    console.log(res) // 成功2
})
实现catch方法
  • 下面开始实现Promise的catch方法,catch方法很简单,catch内部调用then方法把回调函数作为reject的参数执行
// 实现功能
const p = new Promise((resolve, reject) => {
    setTimeout(() => {
        reject('失败')
    }, 2000)
}).then(res => { 
    console.log(res)
}).catch(e => {
    console.log(e) // 失败
})
// 代码实现
// Promise是一个构造函数
class MPromise {
    ...
    catch (fn) {
        // 直接返回then执行之后的结果, 因此catch得到的也是新的Promise
        return this.then(null,fn)
    }
}
  • 接下来,实现Promise的静态方法,all, resolve, reject等
实现一个Promise.resolve(value)
  • 如果value是一个Promise对象,直接返回
  • 如果不是Promise对象,返回一个新的Promise对象,value作为成功状态的值
// 实现功能
Promise.resolve(21).then(res => {
    console.log(res) // 21
})
// 代码实现
// Promise是一个构造函数
class MPromise {
    ...
    static resolve (value) {
        // 如果是一个Promise对象
        if (value instanceof MPromise) {
            return value
        }
        // 否则直接返回一个新的MPromise
        return new MPromise((resolve,reject) => {
            resolve(value)
        })
    }
}
实现一个Promise.reject方法
  • 此方法很简单,直接返回一个新的promise对象,值作为失败的状态的值
// 实现功能
Promise.reject(0).then(res => {
    console.log(res)
},err => {
    console.log(err) // 0
})
// 代码实现
// Promise是一个构造函数
class MPromise {
    ...
    static reject (reason) {
        // 否则直接返回一个新的MPromise
        return new MPromise((resolve,reject) => {
            reject(reason)
        })
    }
}
  • 因此Promise.reject和Promise.resolve有很大的区别,如果给这两个方法都传递一个Promise对象,那么Promise.resolve会返回这个Promise对象,而Reject会返回一个新的对象,最终Promise.resolve得到的结果是Promise对象resolve或reject之后的值,而Reject得到的是一个Promise对象
const p = new MPromise((resolve, reject) => {
    resolve(1)
})
MPromise.resolve(p).then(res => {
    console.log(res) // 1
})
MPromise.reject(p).then(res => {
    console.log(res)
}, err => {
    console.log(err) // MPromise对象
})
实现一个Promise.all方法
// 实现功能
const p1 = new Promise((resolve, reject) => {
    resolve(1)
})
const p2 = new Promise((resolve, reject) => {
    setTimeout(() => {
        resolve(2)
    },2000)
})
const p3 = 333
Promise.all([p1,p2,p3]).then(res => {
    console.log(res) // [1, 2, 333]
})
// 代码实现
// Promise是一个构造函数
class MPromise {
    ...
    static all (arr) {
        // 如果参数不是一个数组就直接报错
        if (!Array.isArray(arr)) {
         throw Error('参数必须是一个数组')
        }
        return new MPromise((resolve, reject) => {
            // 存储执行成功的结果
            const result = []
            // 定义一个计数变量,用来统计所有的成功是否都执行完毕
            let count = 0
            const len = arr.length
            // 遍历数组
            for (let i = 0; i < len; i++) {
                MPromise.resolve(arr[i]).then(res => {
                    // 这里不能使用push,是因为all返回的成功值和参数的顺序为一致的
                    result[i] = res
                    count++
                    // 如果长度相等表示全部执行完毕,通过resolve返回
                    if (count === len) {
                        resolve(result)
                    }
                }, err => {
                    reject(err) // 失败直接reject
                })
            }
        })
    }
}
  • 注意点:
  1. all返回一个新的promise
  2. all中成功的回调获得到结果和传递的参数的顺序为一致的
  3. all中如果有一项抛错,就返回reject,但是参数中的都会被执行一次
  • 完整代码
// 实现功能
const p1 = new Promise((resolve, reject) => {
    resolve(1)
})
const p2 = new Promise((resolve, reject) => {
    setTimeout(() => {
        resolve(2)
    },2000)
})
const p3 = 333
Promise.all([p1,p2,p3]).then(res => {
    console.log(res) // [1, 2, 333]
})
// 代码实现
// 定义三种状态
const PENDING = 'PENDING'
const FULFILLED = 'FULFILLED'
const REJECTED = 'REJECTED'
// Promise是一个构造函数
class MPromise {
    constructor(fn){
        // 定义两个数组分别用来存储pending时候的回调函数
        // 注意这里为什么要用数组,根据规范的then下的第四点,是因为同一个Promise实例可以多次调用then
        this.fulfilledList = []
        this.rejectedList = []
        // 当前状态下的值
        this.value = null
        // 当前状态 初始状态为pending
        this.status = PENDING
        // fn同步执行,把resolve和reject传递进去
        fn(this.resolve.bind(this), this.reject.bind(this))
    }
    // resolve的实现
    resolve (val) {
        // 只有在pending的时候才能改变状态和值
        if (this.status === PENDING) {
            // 改变状态
            this.status = FULFILLED
            // 改变值
            this.value = val
            // 执行回调集合
            this.fulfilledList.forEach(cb => {
                cb()
            })
        }
    }
    // reject的实现
    reject (reason) {
        // 只有在pending的时候才能改变状态和值
        if (this.status === PENDING) {
            this.status = REJECTED
            this.value = reason
            // 执行回调集合
            this.rejectedList.forEach(cb => {
                cb()
            })
        }
    }
    // then方法
    then (onFulfilled, onRejected) {
        // 如果回调函数不是一个函数则忽略。初始一个函数
        onFulfilled = this.isFunction(onFulfilled) ? onFulfilled : (value) => { return value }
        onRejected = this.isFunction(onFulfilled) ? onRejected : (reason) => { throw reason }
        // then方法返回一个新的promise
        const p = new MPromise((resolve, reject) => {
             // 根据规成功和失败的回调是在微任务队列中执行
            const onFulfilledMicroTask = () => {
                queueMicrotask(() => {
                    try{ // try是因为promise中只要报错就走reject
                       const x = onFulfilled(this.value)
                       this.resolvePromise(p, x, resolve, reject)
                    }catch(e){
                        reject(e)
                    }
                })
            }
            const onRejectedMicroTask = () => {
                queueMicrotask(() => {
                   try{
                       const x = onRejected(this.value)
                       this.resolvePromise(p, x, resolve, reject)
                    }catch(e){
                        reject(e)
                    }
                })
            }
            // 根据当前状态判断具体执行成功回调还是失败回调
            switch(this.status){
                case FULFILLED: { // 如果是成功状态就执行成功的回调并把当前值传递进去
                    onFulfilledMicroTask(this.value)
                    break;
                }
                case REJECTED: { // 如果是失败的状态就执行失败的回调把当前值传递进去
                    onRejectedMicroTask(this.value)
                    break;
                }
                case PENDING: { // 如果是pending状态就需要把对应的回调存储起来
                    this.fulfilledList.push(onFulfilledMicroTask)
                    this.rejectedList.push(onRejectedMicroTask)
                    break;
                }
            }
        })
        return p
    }
    // 判断是否是一个函数
    isFunction (fn) {
        return typeof fn === 'function'
    }
    
    // 在resolvePromise中对then中的回调结果进行处理
    resolvePromise (p, x, resolve, reject) {
        // 如果p和x相等需要抛错
        if (p === x) {
           throw 'p和x不能是同一个Promise对象'
        }
        // 如果x是一个Promise
        if (x instanceof MPromise) {
            x.then(res => {
                resolve(res)
            },err => {
                reject(err)
            })
        } else if (typeof x === 'function' || typeof x=== 'object') { // 如果是一个函数或对象
            // 如果是null 直接resolve
            if (x === null) {
               return resolve(null)
            }
            try {
                 const then = x.then
                // 根据规范 如果then是一个函数通过this调用,否则直接resolve返回
                if (typeof then === 'function') {
                    return then.call(this)
                } else {
                    return resolve(then)
                }
            } catch(e) {
                return reject(e)
            }
        } else { // 其他值
            return resolve(x)
        }
    }
    static resolve (value) {
        // 如果是一个Promise对象
        if (value instanceof MPromise) {
            return value
        }
        // 否则直接返回一个新的MPromise
        return new MPromise((resolve,reject) => {
            resolve(value)
        })
    }
    static all (arr) {
        // 如果参数不是一个数组就直接报错
        if (!Array.isArray(arr)) {
         throw Error('参数必须是一个数组')
        }
        return new MPromise((resolve, reject) => {
            // 存储执行成功的结果
            const result = []
            // 定义一个计数变量,用来统计所有的成功是否都执行完毕
            let count = 0
            const len = arr.length
            // 遍历数组
            for (let i = 0; i < len; i++) {
                MPromise.resolve(arr[i]).then(res => {
                    // 这里不能使用push,是因为all返回的成功值和参数的顺序为一致的
                    result[i] = res
                    count++
                    // 如果长度相等表示全部执行完毕,通过resolve返回
                    if (count === len) {
                        resolve(result)
                    }
                }, err => {
                    reject(err) // 失败直接reject
                })
            }
        })
    }
}

// 测试
const p1 = new MPromise((resolve, reject) => {
    resolve(1)
})
const p2 = new MPromise((resolve, reject) => {
    setTimeout(() => {
        resolve(2)
    },2000)
})
const p3 = 333
MPromise.all([p1,p2,p3]).then(res => {
    console.log(res) // [1, 2, 333] 按照顺序输出
})
实现Promise.race方法
  • race方法只要有一个执行就返回
// 实现功能
const p1 = new Promise((resolve, reject) => {
    setTimeout(() => {
        resolve(1)
    },1000)
})
const p2 = new Promise((resolve, reject) => {
    setTimeout(() => {
        resolve(2)
    },2000)
})
const p3 = 333
Promise.race([p1,p2,p3]).then(res => {
    console.log(res) // 333
})
// 代码实现
// Promise是一个构造函数
class MPromise {
    ...
    static race (arr) {
        // 如果参数不是一个数组就直接报错
        if (!Array.isArray(arr)) {
         throw Error('参数必须是一个数组')
        }
        return new MPromise((resolve, reject) => {
            const len = arr.length
            // 遍历数组
            for (let i = 0; i < len; i++) {
                MPromise.resolve(arr[i]).then(res => {
                    console.log(res)
                    resolve(result)
                }, err => {
                    console.log(err)
                    reject(err) // 失败直接reject
                })
            }
        })
    }
}
  • race方法中的参数都会被执行
const p1 = new MPromise((resolve, reject) => {
    setTimeout(() => {
        resolve(1)
    },2300)
})
const p2 = new MPromise((resolve, reject) => {
    setTimeout(() => {
        resolve(2)
    },2000)
})
const p4 = new MPromise((resolve, reject) => {
    setTimeout(() => {
      reject(4)
    },1000)
})
MPromise.race([p1,p2,p4]).then(res => {
    console.log(res) // 4
})
// 结果4 也会输出for循环中的console,分别是2、1 
实现一个allSettled方法

Promise.allSettled([promise1, promise2])等待给定的所有的promise全部执行完成之后,不管是成功还是失败,最终会返回一个数组,数组中的每项都是根据输入的顺序执行之后的结果;如果传递的不是promise对象会通过Promise进行包装;

eg:
Promise.allSettled([1,2,3]).then(res => {
    console.log(res) // [{status: 'fulfilled', value: 1},{status: 'fulfilled', value: 2},{status: 'fulfilled', value: 3}]
})
// 实现
class MPromise {
    ...
    static allSettled (arr) {
        // 如果参数不是一个数组就直接报错
        if (!Array.isArray(arr)) {
         throw Error('参数必须是一个数组')
        }
        return new MPromise((resolve, reject) => {
            const len = arr.length
            const result = []
            // 遍历数组
            for (let i = 0; i < len; i++) {
                MPromise.resolve(arr[i]).then(res => {
                    result[i] = { 
                        status'fulfilled',
                        value: res,
                    }
                }, err => {
                    result[i] = { 
                        status'rejected',
                        value: res,
                    }
                }).finally(() => {
                    i === len - 1 && resolve(result)
                })
            }
        })
    }
}
MPromise.allSettled([MPromise.resolve(1), MPromise.reject(22)]).then(res => {
    console.log(res) // [{status: 'fulfilled', value: 1},{status: 'rejected', value: 22}]
})
实现finally方法

finally不管成功还是失败都是执行,并且返回一个新的promise对象;

finally (callback) {
    return this.then(data => {
      return MPromise.resolve(callback()).then(() => {
        return data
      })
    }, err => {
      return MPromise.reject(callback()).then(() => { throw err })
    })
}
相关面试题
  • 面试题,打印出的Promise对象的状态分别是什么
const p = new MPromise((res,rej) => {
   setTimeout(() => {
       rej(1)
   },3000)
}).then(null, err => {
   console.log(p) // 
})

  setTimeout(() => {
     console.log(p) //
  },5000)
  • 第一个输出的对象的状态应该是pending
  • 第二个输出的对象的状态是fulfilled
  • 解析:因此then返回的是一个新的Promise,而then回调的执行是在新Promise的resolve或reject之前,因此此时的状态还是pending
const onRejectedMicroTask = () => {
    queueMicrotask(() => {
       try{
           // 这开始执行回调,而resolve或reject是在resolvePromise中执行,
           // 因此此时的状态还是pending
           const x = onRejected(this.value)
           this.resolvePromise(p, x, resolve, reject)
        }catch(e){
            reject(e)
        }
    })
}
  • 面试题,下面代码输出什么?
    Promise.resolve(1).then(2).then(3).then(console.log)

解析:

  • Promise.resolve(1)执行之后,此时promise对象(标记为p1)的状态从pedding变为fulfilled,值为1;

  • 再执行then(2)的时候,因为参数不是一个函数,内部会创建两个函数分别是成功和失败(微任务),返回一个新的promise对象,这里标记为p2;

  • 又调用then(3),因为参数不是一个函数,内部会创建两个函数分别是成功和失败(微任务),返回一个新的promise对象,这里标记为p3;

  • 又执行了then(console.log),传递了console.log这个函数(微任务)

  • 执行栈执行完毕,此时开始执行微任务队列

  • 因为p1的状态是fulfilled,因此会执行成功的函数并且把当前值1传递进去,函数把当前的值1返回,此时没有错误,所以p2的resolve(1)执行,此时p2的状态从pedding变为fulfilled,值为1;

  • 因为p2的状态是fulfilled,因此会执行成功的函数并且把当前值1传递进去,函数把当前的值1返回,此时没有错误,所以p3的resolve(1)执行,此时p3的状态从pedding变为fulfilled,值为1;

  • 因为p3的状态是fulfilled,因此会执行成功的函数console.log并且把当前值1传递进去,因此最后输出1

  • 答案: 1

  • 面试题:下面输出什么?

var a;
var b = new Promise((resolve,reject) => {
    console.log('promise1')
    setTimeout(() => {
        resolve()
    },1000)
}).then(() => {
    console.log('promise2')
}).then(() => {
    console.log('promise3')
}).then(() => {
    console.log('promise4')
})

a = new Promise(async (resolve,reject) => {
    console.log(a)
    await b
    console.log(a)
    await a
    resolve()
    console.log('after')
})
console.log('end')

解析:

  • 先执行第一个new Promise,其中的console.log('promise1')输出promise1,setTimeout宏任务一秒之后执行,返回一个Promise实例,此时的状态为pending;
  • 执行第一个then,返回一个新的Promise对象p1,把成功的回调放入微任务队列中,此时的状态为pending;
  • 执行第二个then,返回一个新的Promise对象p2,把成功的回调放入微任务队列中,此时的状态为pending;
  • 执行第三个then,返回一个新的Promise对象p3,把成功的回调放入微任务队列中,此时的状态为pending;
  • 第三个then之后完毕,b = p3,此时的状态为pending;
  • 执行第二个new Promise,console.log(a)此时的new Promise还未执行完毕,因此a是undefined,所以输出undefined;
  • 执行await b,要等待b的resolve执行,此时第二个new Promise执行完毕,返回一个promise对象pa,状态为pending
  • 执行console.log('end'),输出end
  • 一秒之后执行setTimeout(() => { resolve() },1000),第一个new Promise的状态变为fulfilled,执行第一个then的成功回调,输出promise2,p1的状态状态变为fulfilled;执行第二个then的成功回调,输出promise3,p2的状态状态变为fulfilled;执行第三个then的成功回调,输出promise4,p3的状态状态变为fulfilled;
  • await b执行完毕,执行console.log(a),此时的a就是pa,状态为pending;
  • 执行await a,此时a的状态为pending,因此一直处于等待状态,不再继续往下执行 答案: promise1,undefined,end,promise2,promise3,promise4,Promise对象状态为pending
  • 面试题:
async function m1(){
    return 1
}
async function m2(){
    const n = await m1()
    console.log(n)
    return 2
}
async function m3(){
    const n = m2()
    console.log(n)
    return 3
}
m3().then(res => {
    console.log(res)
})
m3()
console.log(4)

解析:

  • 执行 m3(),m3中执行了m2,m2中执行了m1,m1是async,因此此时返回promise对象p1,状态为fulfilled,成功的回调放入微任务队列中,m2返回一个promise对象p2,状态为pending,因此m2()执行完毕之后,console.log(n)n为P2状态为pending,m3执行完返回一个Promise对象p3,状态为fulfilled,成功的回调放入微任务队列中
  • 再此执行m3(),m3中执行了m2,m2中执行了m1,m1是async,因此此时返回promise对象p1',状态为fulfilled,成功的回调放入微任务队列中,m2返回一个promise对象p2',状态为pending,因此m2()执行完毕之后,console.log(n)n为P2'状态为pending,m3执行完返回一个Promise对象p3',状态为fulfilled,成功的回调放入微任务队列中
  • console.log(4) 输出4
  • 执行微任务,p1的成功回调执行,await m1()返回1,因此输出console.log(n)为1,执行p3的回调console.log(res)输出3;执行p1'的成功回调执行,await m1()返回1,因此输出console.log(n)为1
  • 答案:promise状态为pending,promise状态为pending, 4,1,3,1