/ JavaScript / 3782浏览

手写一个Promise

PromiseA+规范

状态机制

[[PromiseState]] 有三种状态,fulfilledrejectedpending
初始值为pending
状态值一旦改变就不能再改变。

class MyPromise {
    static PENDING = 'pending'
    static FULFILLED = 'fulfilled'
    static REJECTED = 'rejected'
    constructor() {
        this.state = MyPromise.PENDING
    }
}

MyPromise接受一个回调函数,回调函数接受两个方法,第一个是resolve,第二个是reject
resolvereject都会接受一个表示结果原因的参数。
[[PromiseResult]]初始值为null。

class MyPromise {
    static PENDING = 'pending'
    static FULFILLED = 'fulfilled'
    static REJECTED = 'rejected'
    constructor(executor) {
        this.state = MyPromise.PENDING
        this.result = null // 结果
        executor(this.resolve, this.reject)
    }
    resolve(result) {
        if (this.state === MyPromise.PENDING) {
            this.state = MyPromise.FULFILLED
            this.result = result
        }
    }
    reject(reason) {
        if (this.state === MyPromise.PENDING) {
            this.state = MyPromise.REJECTED
            this.result = reason
        }
    }
}

resolvereject被作为回调参数传入executor调用时this绑定会隐性丢失,因为我们知道函数中的this最终会指向调用此函数时的对象。
在严格模式下是undefined,非严格模式下会指向window对象。

let p = new MyPromise((resolve, reject) => {
    // ...
    resolve('ok') // 调用时this绑定会隐性丢失
})

所以需要将它们的this绑定

class MyPromise {
    constructor(executor) {
        // ...
        executor(this.resolve.bind(this), this.reject.bind(this))
    }
}

如果executor执行出错,需要reject掉。

class MyPromise {
    static PENDING = 'pending'
    static FULFILLED = 'fulfilled'
    static REJECTED = 'rejected'
    constructor(executor) {
        this.state = MyPromise.PENDING
        this.result = null // 结果
        try {
            executor(this.resolve.bind(this), this.reject.bind(this))
        } catch (e) {
            this.reject(this.result)
        }
    }
    resolve(result) {
        if (this.state === MyPromise.PENDING) {
            this.state = MyPromise.FULFILLED
            this.result = result
        }
    }
    reject(reason) {
        if (this.state === MyPromise.PENDING) {
            this.state = MyPromise.REJECTED
            this.result = reason
        }
    }
}

then方法实现

then接受两个参数onFulFilled,onRejected
then方法是异步执行的。规范中是用的微任务执行,但是JavaScript没有能直接操控微任务队列的方法,但是可以用宏任务setTimeout代替。
then方法调用的时候状态可能有三种情况,也就是对应着状态机制中的三种状态,需要分别作处理。

class MyPromise {
    // ...
    then(onFulfilled, onRejected) {
        if (this.state === MyPromise.FULFILLED) {
            // 状态变为fulfilled时执行onFulfilled
            setTimeout(() => {
                onFulfilled(this.result)
            })
        } else if (this.state === MyPromise.REJECTED) {
            // 状态变为fulfilled时执行onRejected
            setTimeout(() => {
                onRejected(this.result)
            })
        } else {
            // ...
        }
    }
}

当调用then方法resolve,reject还未执行,就是executor中执行了异步的操作,state的状态还为pending时,我们需要让onFulfilledonRejected回调在恰当的时机,即state状态变更为fulfilledrejected时才执行。
也就是说then方法调用时状态为pending的话,就要让onFulfilledonRejectedresolve,reject方法调用的时候才去执行。

class MyPromise {
    constructor(executor) {
        // ...
        this.onFulfilledCallbacks = []
        this.onRejectedCallbacks = []
    }
     resolve(result) {
        if (this.state === MyPromise.PENDING) {
            this.state = MyPromise.FULFILLED
            this.result = result
            // 执行pending时传入的onFulfilled
            this.onFulfilledCallbacks.forEach(callback => {
                callback(result)
            })
        }
    }
    reject(reason) {
        if (this.state === MyPromise.PENDING) {
            this.state = MyPromise.REJECTED
            this.result = reason
            // 执行pending时传入的onRejected
            this.onRejectedCallbacks.forEach(callback => {
                callback(reason)
            })
        }
    }
    then(onFulfilled, onRejected) {
        if (this.state === MyPromise.FULFILLED) {
            // 状态变为fulfilled时执行onFulfilled
            setTimeout(() => {
                onFulfilled(this.result)
            })
        } else if (this.state === MyPromise.REJECTED) {
            // 状态变为fulfilled时执行onRejected
            setTimeout(() => {
                onRejected(this.result)
            })
        } else {
            // 将两个回调保存起来,等待恰当的时机调用
            this.onFulfilledCallbacks.push(() => {
                setTimeout(() => {
                    onFulfilled(this.result)
                })
            })
            this.onRejectedCallbacks.push(() => {
                setTimeout(() => {
                    onRejected(this.result)
                })
            })
        }
    }
}

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.

2.2.7.1 如果 onFulfilled 或者 onRejected 返回一个值 x ,则运行[[Resolve]](promise2, x)
2.2.7.2 如果 onFulfilled 或者 onRejected 抛出一个异常 e ,则 promise2 必须以 e 作为原因拒绝
2.2.7.3 如果 onFulfilled 不是函数且 promise1 成功执行, promise2 必须成功执行并返回相同的值
2.2.7.4 如果 onRejected 不是函数且 promise1 拒绝执行, promise2 必须拒绝执行并返回相同的据因

then链式调用

class MyPromise {
    constructor(executor) {
        // ...
        this.onFulfilledCallbacks = []
        this.onRejectedCallbacks = []
    }
     resolve(result) {
        if (this.state === MyPromise.PENDING) {
            this.state = MyPromise.FULFILLED
            this.result = result
            // 执行pending时传入的onFulfilled
            this.onFulfilledCallbacks.forEach(callback => {
                callback(result)
            })
        }
    }
    reject(reason) {
        if (this.state === MyPromise.PENDING) {
            this.state = MyPromise.REJECTED
            this.result = reason
            // 执行pending时传入的onRejected
            this.onRejectedCallbacks.forEach(callback => {
                callback(reason)
            })
        }
    }
    then(onFulfilled, onRejected) {
        if (this.state === MyPromise.FULFILLED) {
            // 状态变为fulfilled时执行onFulfilled
            setTimeout(() => {
                try {
                    if (typeof onFulfilled !== 'function') {
                        // 2.2.7.3 如果 onFulfilled 不是函数且 promise1 成功执行, promise2 必须成功执行并返回相同的值
                        resolve(this.result)
                    } else {
                         // 2.2.7.1 如果 onFulfilled 或者 onRejected 返回一个值 x ,则运行[[Resolve]](promise2, x)
                        let x = onFulfilled(this.result)
                        resolvePromise(promise2, x)
                    }
                } catch (e) {
                    // 2.2.7.2 如果 onFulfilled 或者 onRejected 抛出一个异常 e ,则 promise2 必须以 e 作为原因拒绝
                    reject(e)
                }
            })
        } else if (this.state === MyPromise.REJECTED) {
            // 状态变为fulfilled时执行onRejected
             setTimeout(() => {
                    try {
                        if (typeof onRejected !== 'function') {
                            // 2.2.7.4 如果 onRejected 不是函数且 promise1 拒绝执行, promise2 必须拒绝执行并返回相同的据因
                            reject(this.result)
                        } else {
                            // 2.2.7.1 如果 onFulfilled 或者 onRejected 返回一个值 x ,则运行[[Resolve]](promise2, x)
                            let x = onRejected(this.result)
                            resolvePromise(promise2, x)
                        }
                    } catch (e) {
                        // 2.2.7.2 如果 onFulfilled 或者 onRejected 抛出一个异常 e ,则 promise2 必须以 e 作为原因拒绝
                        reject(e)
                    }
                });
        } else {
            // 将两个回调保存起来,等待恰当的时机调用
            this.onFulfilledCallbacks.push(() => {
                setTimeout(() => {
                    onFulfilled(this.result)
                })
            })
            this.onRejectedCallbacks.push(() => {
                setTimeout(() => {
                    onRejected(this.result)
                })
            })
        }
    }
}

接下来实现resolvePromise

根据规范的描述

function resolvePromise(promise, x) {
    // x等于promise抛出异常,为了防止无限循环
    if (x === promise) {
        throw new TypeError('错啦')
    }

    // 如果x是一个promise,那就继续执行
    if (x instanceof MyPromise) {
        x.then((y) => {
            resolvePromise(promise, y)
        }, (r) => {
            promise.reject(r)
        })
    } else if (Object.prototype.toString.call(x) === '[object Object]' || typeof x === 'function') {
        // 如果是对象或者方法,为了兼容有then方法的类似promise的对象或方法
        let then
        try {
            then = x.then
        } catch (e) {
            return promise.reject(e)
        }

        // 2.3.3.3 如果 then 是函数,将 x 作为函数的作用域 this 调用之。传递两个回调函数作为参数,第一个参数叫做 resolvePromise ,第二个参数叫做 rejectPromise
        if (typeof then === 'function') {
            let called = false
            // 2.3.3.3.1 如果 resolvePromise 以值 y 为参数被调用,则运行 [[Resolve]](promise, y)
            try {
                then.call(
                    x,
                    // 2.3.3.3.1 如果 resolvePromise 以值 y 为参数被调用,则运行 [[Resolve]](promise, y)
                    y => {
                        // 2.3.3.3.3 如果 resolvePromise 和 rejectPromise 均被调用,或者被同一参数调用了多次,则优先采用首次调用并忽略剩下的调用    
                        if (called) return
                        called = true
                        resolvePromise(promise, y)
                    },
                    // 2.3.3.3.2 如果 rejectPromise 以据因 r 为参数被调用,则以据因 r 拒绝 promise
                    r => {
                        // 2.3.3.3.3 如果 resolvePromise 和 rejectPromise 均被调用,或者被同一参数调用了多次,则优先采用首次调用并忽略剩下的调用
                        if (called) return
                        called = true
                        promise.reject(r)
                    }
                )
            } catch (e) {
                // 2.3.3.3.4 如果调用 then 方法抛出了异常 e
                // 2.3.3.3.4.1 如果 resolvePromise 或 rejectPromise 已经被调用,则忽略之
                // 2.3.3.3.4.2 否则以 e 为据因拒绝 promise
                if (called) return
                called = true
                promise.reject(e)
            }

        } else {
            // 2.3.3.4 如果 then 不是函数,以 x 为参数执行 promise
            promise.resolve(x)
        }
    } else {
        // 2.3.3.4 如果 then 不是函数,以 x 为参数执行 promise
        promise.resolve(x)
    }
}

最后优化一下then中的条件分支
最终版

class MyPromise {
    static PENDING = 'pending'
    static FULFILLED = 'fulfilled'
    static REJECTED = 'rejected'
    constructor(executor) {
        this.state = MyPromise.PENDING
        this.result = null
        this.onFulfilledCallbacks = []
        this.onRejectedCallbacks = []
        try {
            executor(this.resolve.bind(this), this.reject.bind(this))
        } catch (e) {
            this.reject(this.result)
        }
    }
    resolve(result) {
        if (this.state === MyPromise.PENDING) {
            this.state = MyPromise.FULFILLED
            this.result = result
            this.onFulfilledCallbacks.forEach(callback => {
                callback(result)
            })
        }
    }
    reject(reason) {
        if (this.state === MyPromise.PENDING) {
            this.state = MyPromise.REJECTED
            this.result = reason
            this.onRejectedCallbacks.forEach(callback => {
                callback(reason)
            })
        }
    }
    then(onFulfilled, onRejected) {
        let promise2 = new MyPromise((resolve, reject) => {
            const handler = (fn, state) => {
                setTimeout(() => {
                    try {
                        // 2.2.7.3 如果 onFulfilled 不是函数且 promise1 成功执行, promise2 必须成功执行并返回相同的值
                        if (typeof fn !== 'function') {
                            if (state === MyPromise.FULFILLED) {
                                resolve(this.result)
                            } else if (state === MyPromise.REJECTED) {
                                reject(this.result)
                            }
                        } else {
                            // 2.2.7.1 如果 onFulfilled 或者 onRejected 返回一个值 x ,则运行[[Resolve]](promise2, x)
                            let x = fn(this.result)
                            resolvePromise(promise2, x)
                        }
                    } catch (e) {
                        // 2.2.7.2 如果 onFulfilled 或者 onRejected 抛出一个异常 e ,则 promise2 必须以 e 作为原因拒绝
                        reject(e)
                    }
                })
            }
            if (this.state === MyPromise.FULFILLED) {
                    handler(onFulfilled, MyPromise.FULFILLED)
            } else if (this.state === MyPromise.REJECTED) {
                    handler(onRejected, MyPromise.REJECTED)
            } else {
                this.onFulfilledCallbacks.push(() => {
                    handler(onFulfilled, MyPromise.FULFILLED)
                })
                this.onRejectedCallbacks.push(() => {
                    handler(onRejected, MyPromise.REJECTED)
                })
            }
        })
        return promise2
    }
}
function resolvePromise(promise, x) {
    // x等于promise抛出异常,为了防止无限循环
    if (x === promise) {
        throw new TypeError('错啦')
    }

    // 如果x是一个promise,那就继续执行
    if (x instanceof MyPromise) {
        x.then((y) => {
            resolvePromise(promise, y)
        }, (r) => {
            promise.reject(r)
        })
    } else if (Object.prototype.toString.call(x) === '[object Object]' || typeof x === 'function') {
        // 如果是对象或者方法,为了兼容有then方法的类似promise的对象或方法
        let then
        try {
            then = x.then
        } catch (e) {
            return promise.reject(e)
        }

        // 2.3.3.3 如果 then 是函数,将 x 作为函数的作用域 this 调用之。传递两个回调函数作为参数,第一个参数叫做 resolvePromise ,第二个参数叫做 rejectPromise
        if (typeof then === 'function') {
            let called = false
            // 2.3.3.3.1 如果 resolvePromise 以值 y 为参数被调用,则运行 [[Resolve]](promise, y)
            try {
                then.call(
                    x,
                    // 2.3.3.3.1 如果 resolvePromise 以值 y 为参数被调用,则运行 [[Resolve]](promise, y)
                    y => {
                        // 2.3.3.3.3 如果 resolvePromise 和 rejectPromise 均被调用,或者被同一参数调用了多次,则优先采用首次调用并忽略剩下的调用    
                        if (called) return
                        called = true
                        resolvePromise(promise, y)
                    },
                    // 2.3.3.3.2 如果 rejectPromise 以据因 r 为参数被调用,则以据因 r 拒绝 promise
                    r => {
                        // 2.3.3.3.3 如果 resolvePromise 和 rejectPromise 均被调用,或者被同一参数调用了多次,则优先采用首次调用并忽略剩下的调用
                        if (called) return
                        called = true
                        promise.reject(r)
                    }
                )
            } catch (e) {
                // 2.3.3.3.4 如果调用 then 方法抛出了异常 e
                // 2.3.3.3.4.1 如果 resolvePromise 或 rejectPromise 已经被调用,则忽略之
                // 2.3.3.3.4.2 否则以 e 为据因拒绝 promise
                if (called) return
                called = true
                promise.reject(e)
            }

        } else {
            // 2.3.3.4 如果 then 不是函数,以 x 为参数执行 promise
            promise.resolve(x)
        }
    } else {
        // 2.3.3.4 如果 then 不是函数,以 x 为参数执行 promise
        promise.resolve(x)
    }
}

catch

catch就是then(null, noReject)的语法糖。

class MyPromise {
    // ...
    catch(onReject) {
        return this.then(null, onReject)
    }
}

finally

finally不管状态如何都会执行也不会接受任何值。

class MyPromise {
    // ...
    finally(onFinally) {
        return this.then((result) => {
            // 回调里返回的是Promise需要等待
           return MyPromise.resolve(onFinally()).then(() => result)
        }, (reason) => {
           return MyPromise.resolve(onFinally()).then(() => reason)
        })
    }
}

Promise.all

Promise.all接受一个可迭代对象,当所有promise的状态都变为fulfilled时,才改变自身状态为fulfilled并返回一个传入的迭代对象的值的数组。

class MyPromise {
    // ...
    static all(promiseAry) {
        let promiseIns = new MyPromise((resolve, reject) => {
            let _pAryLen = promiseAry.length,
                // 迭代对象
                _pAry = Array.from(promiseAry),
                // 结果
                _pAryResults = new Array(_pAryLen),
                _isResolveAll = (() => {
                    let count = 0
                    return () => _pAryLen === ++count
                })(),
                _c = (r, i) => {
                    // 保持值的位置顺序一致
                    _pAryResults[i] = r
                    _isResolveAll() && 
                        resolve(_pAryResults)
                },
                _f = (e) => {
                    reject(e)
                };
            _pAry.forEach((p, i) => {
                p.then((r) => _c(r, i)).catch(_f)
            })
        })
        return promiseIns
    }
}

Promise.race

Promise.race接受一个可迭代对象,当其中一个promise的状态变为fulfilled时,才改变自身状态为fulfilled并返回这个对象的值。

class MyPromise {
    // ...
    static race(promiseAry) {
        let promiseIns = new MyPromise((resolve, reject) => {
            let _pAry = Array.from(promiseAry)
            _pAry.forEach(p => {
                p.then((r => {
                    resolve(r)
                })).catch(e => {
                    reject(e)
                })
            })
        })
        return promiseIns
    }
}
更新于
JavaScript中的this到底指向谁?
JavaScript中的this到底指向谁?
手写一个call、apply、bind
手写一个call、apply、bind
防抖(debounce)、节流(throttle)原理与实现
防抖(debounce)、节流(throttle)原理与实现
JS如何等待多个异步操作完成后再进行操作
JS如何等待多个异步操作完成后再进行操作
原型
原型

0

  1. This post has no comment yet

发表回复