Promise ES6 实现(完美符合Promise/A+规范)

285

本文编写的原因:为什么没有能通过测例的基于ES6(class)实现Promise?!

主要记录面试高频问题,手写一个Promise...

好吧,“完美”标题党了。。。哈哈哈哈哈~

Promise A+ 的ES6实现

直接略过Promise是什么与基本使用,直接记录ES6(class)的Promise实现

const isFunction = fn => typeof fn === "function"

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

class FwzPromise {
    constructor(executor) {
        if (new.target !== FwzPromise) throw TypeError('undefined is not a promise')
        if (!isFunction(executor)) throw TypeError(`Promise resolver ${executor} is not a function`)
        
        this.status = PENDING
        this.value = undefined
        this.reason = undefined
        this.onFulfilledArr = []
        this.onRejectedArr = []

        //PromiseA+ 2.1
        const resolve = (value) => {
            if (this.status === PENDING) {
                this.status = FULFILLED
                this.value = value 
                this.onFulfilledArr.forEach(fn => fn()) //PromiseA+ 2.2.6.1
            }
        }
    
        const reject = (reason) => {
            if (this.status === PENDING) {
                this.status = REJECTED
                this.reason = reason
                this.onRejectedArr.forEach(fn => fn()) //PromiseA+ 2.2.6.2
            }
        }

        try {
            executor(resolve, reject)
        } catch(e) {
            reject(e)
        }
    }

    // https://stackoverflow.com/questions/2130241/pass-correct-this-context-to-settimeout-callback
    // https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Global_Objects/Function/bind
    // 浏览器中,setTimeout 执行函数的上下文为 window
    // es6 : setTimeout(() => this.fn())
    // es5 : setTimeout(fn.bind(this))
    then(onFulfilled, onRejected) {
        //PromiseA+ 2.2.1 / PromiseA+ 2.2.5 / PromiseA+ 2.2.7.3 / PromiseA+ 2.2.7.4
        onFulfilled = isFunction(onFulfilled) ? onFulfilled : value => value
        onRejected = isFunction(onRejected) ? onRejected : reason => { throw reason }
        
        // 已用箭头函数绑定, 仅留作说明
        const self = this
        const nextPromise = new FwzPromise((resolve, reject) => {
            // 箭头函数无法改变 this指向, 所以
            // 在内部this为 声明箭头函数() => { try... }的 this,即执行setTimeout的上下文(this)
            // 而执行setTimeout的上下文(this)为声明箭头函数 fulfilledHandler 的this
            // 箭头函数 fulfilledHandler 的this绑定为原外层的promise, 即self
            const fulfilledHandler = () => {
                setTimeout(() => {
                    try {
                        //PromiseA+ 2.2.7.1
                        const x = onFulfilled(this.value)
                        this.resolvePromise(nextPromise, x, resolve, reject)
                    } catch (e) {
                        //PromiseA+ 2.2.7.2
                        reject(e)
                    }
                })
            }
            
            const rejectedHandler = () => {
                setTimeout(() => {
                    try {
                        const x = onRejected(this.reason)
                        this.resolvePromise(nextPromise, x, resolve, reject)
                    } catch(e) {
                        reject(e)
                    }
                })
            }

            if (self.status === FULFILLED) {
                //PromiseA+ 2.2.2
                //PromiseA+ 2.2.4 --- setTimeout
                fulfilledHandler()
            } else if (this.status === REJECTED) {
                //PromiseA+ 2.2.3
                rejectedHandler()
            } else if (this.status === PENDING) {
                this.onFulfilledArr.push(fulfilledHandler)
                this.onRejectedArr.push(rejectedHandler)
            }
        })

        return nextPromise
    }

    // 如果要完全模拟ES6的Promise,需要将resolvePromise设置为函数内函数
    resolvePromise(nextPromise, x, resolve, reject) {
        //PromiseA+ 2.3.1
        if (nextPromise === x) reject(new TypeError('Chaining cycle'))
    
        if (x && typeof x === 'object' || typeof x === 'function') {
            let used //PromiseA+2.3.3.3.3 只能调用一次
            try {
                let then = x.then
                if (isFunction(then)) {
                    //PromiseA+2.3.3
                    then.call(x, (y) => {
                        //PromiseA+2.3.3.1
                        if (used) return 
                        used = true 
                        this.resolvePromise(nextPromise, y, resolve, reject)
                    }, (r) => {
                        //PromiseA+2.3.3.2
                        if (used) return 
                        used = true 
                        reject(r)
                    })
                } else {
                    //PromiseA+2.3.3.4
                    if (used) return 
                    used = true 
                    resolve(x)
                }
            } catch (e) {
                //PromiseA+ 2.3.3.2
                if (used) return 
                used = true 
                reject(e)
            }
        } else {
            //PromiseA+ 2.3.3.4
            resolve(x)
        }
    }  

    catch(onRejected) {
        return this.then(null, onRejected)
    }

    finally(cb) {
        return this.then(
            (value) => FwzPromise.resolve(cb()).then(() => value),
            (reason) =>
                FwzPromise.resolve(cb()).then(() => {
                    throw reason;
                })
        );
    }

    static resolve(value) {
        if (value instanceof FwzPromise) return value;
        return new FwzPromise((resolve) => resolve(value));
    }

    static reject(value) {
        return new FwzPromise((resolve, reject) => reject(value));
    }

    static all(arr) {
        return new FwzPromise((resolve, reject) => {
            const result = [];
            let count = arr.length;
            for (let [i, p] of arr.entries()) {
                // 数组参数如果不是FwzPromise实例,先调用FwzPromise.resolve
                FwzPromise.resolve(p).then(
                    (res) => {
                        result[i] = res;
                        // 所有状态都变成fulfilled时返回的FwzPromise状态就变成fulfilled
                        if (--count === 0) resolve(result);
                    },
                    // 有一个被rejected时返回的FwzPromise状态就变成rejected
                    reject
                );
            }
        });
    }

    static race(arr) {
        return new FwzPromise((resolve, reject) => {
            for (let p of arr) {
                // 返回第一个settled的Promise
                FwzPromise.resolve(p).then(resolve, reject)
            }
        });
    }

    static allSettled(arr) {
        // allSettled返回的 Promise 为 fulfilled 状态
        return new FwzPromise((resolve, reject) => {
            const result = []
            let count = arr.length
            for (let [i, p] of arr.entries()) {
                FwzPromise.resolve(p).then(
                    (value) => {
                        result[i] = value
                        if (--count === 0) resolve(result)
                    },
                    (reason) => {
                        result[i] = reason 
                        if (--count === 0) resolve(result)
                    }
                )
            }
        })
    }
}

module.exports = FwzPromise

准确性检验

以promises-aplus-tests的872测例为准

// npm install -g promises-aplus-tests
// promises-aplus-tests promise.js

// 添加测试接口
FwzPromise.defer = FwzPromise.deferred = function () {
    let dfd = {};
    dfd.promise = new FwzPromise((resolve, reject) => {
        dfd.resolve = resolve;
        dfd.reject = reject;
    });
    return dfd;
}

Promise 标准

Promise定义:请查看MDN 或 更详细的说明ECMA-262
Promise A+ :标准, 参考链接的翻译

参考

Promise的源码实现(完美符合Promise/A+规范)