Promise之Promise的实现

262 阅读2分钟

依据Promises/A+规范,实现Promise。

const PENDING = 'pending'
const FULFILLED = 'fulfilled'
const REJECTED = 'rejected'

class Promise {
    constructor(executor) {
        this.status = PENDING
        this.value = undefined
        this.reason = undefined
        this.resolvecbs = []
        this.rejectcbs = []
        let resolve = value => {
            if (this.status === PENDING) {
                this.value = value;
                this.status = FULFILLED;
                this.resolvecbs.forEach(cb => cb())
            }
        }
        let reject = reason => {
            if (this.status === PENDING) {
                this.reason = reason
                this.status = REJECTED
                this.rejectcbs.forEach(cb => cb())
            }
        }
        try {
            executor(resolve, reject)
        } catch (e) {
            reject(e)
        }
    }
    then(onFulfilled, onRejected) {
        onFulfilled = typeof onFulfilled === 'function' ? onFulfilled : value => value;
        onRejected = typeof onRejected === 'function' ? onRejected : err => { throw err }
        let promise2 = new Promise((resolve, reject) => {
            let resolveTask = () => {
                setTimeout(() => {
                    try {
                        let x = onFulfilled(this.value);
                        resolvePromise(promise2, x, resolve, reject)
                    } catch (err) {
                        reject(err)
                    }
                }, 0)
            }

            let rejectTask = () => {
                setTimeout(() => {
                    try {
                        let x = onRejected(this.reason)
                        resolvePromise(promise2, x, resolve, reject)
                    } catch (err) {
                        reject(err)
                    }
                }, 0)
            }

            if (this.status === FULFILLED) {
                resolveTask()
            }
            if (this.status === REJECTED) {
                rejectTask()
            }
            if (this.status === PENDING) {
                this.resolvecbs.push(resolveTask)
                this.rejectcbs.push(rejectTask)
            }
        })
        return promise2
    }

    catch(callback) {
        return this.then(undefined, callback)
    }
    finally(callback) {
        return this.then(
            value => Promise.resolve(callback()).then(() => value),
            err => Promise.resolve(callback()).then(() => { throw err })
        )
    }

    static resolve(p) {
        if (p instanceof Promise) return p;
        if (p !== null && (typeof p === 'object' || typeof p === 'function')) {
            let then = p.then;
            if (typeof then === 'function') {
                return new Promise((resolve, reject) => {
                    then.call(p, value => {
                        resolve(value)
                    })
                })
            }
        }
        return new Promise((resolve, reject) => {
            resolve(p)
        })
    }

    static reject(p) {
        return new Promise((resolve, reject) => {
            reject(p)
        })
    }

    static all(promises) {
        return new Promise((resolve, reject) => {
            let len = promises.length, count = 0;
            let result = new Array(len);
            promises.forEach((p, i) => {
                Promise(p).then(res => {
                    result[i] = res;
                    count++;
                    if(count === len){
                        resolve(result)
                    }
                }, reject)
            })
        })
    }

    static race(promises){
        return new Promise((resolve, reject) => {
            promises.forEach(p => {
                Promise(p).then(resolve, reject)
            })
        })
    }

    static allSettled(promises){
        return new Promise((resolve, reject) => {
            let len = promises.length, count = 0;
            let result = new Array(len)
            promises.forEach((p, i) => {
                Promise.resolve(p).then(res => {
                    result[i] = {
                        status: FULFILLED,
                        value: res
                    }
                    count++;
                    if(count === len) resolve(result)
                }, err => {
                    result[i] = {
                        status: REJECTED,
                        reason: err
                    }
                    count++
                    if(count === len) resolve(result)
                })
            })
        })
    }

    static any(promises){
        return new Promise((resolve, reject) => {
            let len = promises.length, count = 0;
            let result = new Array(len);
            promises.forEach((p, i) => {
                Promise.resolve(p).then(resolve, err => {
                    result[i] = err;
                    count++;
                    if(count === len){
                        // reject(new AggregateError())
                        // 这里用Promise测试如果全部rejected的话只会打印 AggregateError: All promises were rejected
                        // https://es6.ruanyifeng.com/#docs/promise#Promise-any这里应该是有问题的
                        reject('AggregateError: All promises were rejected')
                    }
                })
            })
        })
    }
}

function resolvePromise(promise, x, resolve, reject) {
    if (promise === x) {
        throw new TypeError('Chaining dected cycle in promise!')
    }
    let called;
    if (x !== null && (typeof x === 'object' || typeof x === 'function')) {
        try {
            let then = x.then;
            if (typeof then === 'function') {
                then.call(x, y => {
                    if (called) return
                    called = true
                    resolvePromise(promise, y, resolve, reject)
                }, err => {
                    if (called) return;
                    called = true
                    reject(err)
                })
            } else {
                resolve(x)
            }
        } catch (error) {
            if (called) return;
            called = true
            reject(error)
        }
    } else {
        resolve(x)
    }
}

使用promises-aplus-tests进行测试。以下为测试代码:

let promiseAPlusTest = require('promises-aplus-tests');
Promise.defer = Promise.deferred = function () {
    let obj = {}
    obj.promise = new Promise((resolve, reject) => {
        obj.resolve = resolve
        obj.reject = reject
    })
    return obj
}

promiseAPlusTest(Promise, function (e) {
    console.log(e)
})

以上全部内容,如有疑问,欢迎指正。

10.webp