你该知道的promise(2):实现一个promise

70 阅读2分钟

本文已参与「新人创作礼」活动,一起开启掘金创作之路。

本文将实现一个符合Promise/A+规范的Promise类(目前只有then方法) 具体规范参考上篇文章: 你该知道的promise(1):A+规范中英文对照翻译 在这里插入图片描述

/**
 * 规范2.1.
 */
const PENDING = 'PENDING';
const FULFILLED = 'FULFILLED';
const REJECTED = 'REJECTED';

class MyPromise {
    constructor(fun) {
        this.status = PENDING;
        this.value = null;
        this.reason = null;
        this.resolveCallbacks = [];
        this.rejectCallbacks = [];

        try {
            fun(this.resolve, this.reject)
        } catch (e) {
            this.reject(e)
        }
    }

    resolve = (v) => {
        // 2.1.1
        if(this.status === PENDING) {
            this.status = FULFILLED;
            this.value = v;
            // 按顺序调用resolveCallbacks
            this.resolveCallbacks.forEach(cb => cb(v))
        }
    }

    reject = (r) => {
        // 2.1.1
        if(this.status === PENDING) {
            this.status = REJECTED;
            this.reason = r;
            // 按顺序调用rejectCallbacks
            this.rejectCallbacks.forEach(cb => cb(r))
        }
    }
}

/**
 * 规范2.2.
 * 应该始终返回一个Promise对象确保可以使用then无限调下去
 * 虽然resolve是同步执行的,我们必须保证then是异步调用的,
 * 
 * @param {*} onFulfilled 
 * @param {*} onRejected 
 */
MyPromise.prototype.then = function(onFulfilled, onRejected) {
    let promise2;
    // 2.2.1
    onFulfilled = typeof onFulfilled == 'function' ? onFulfilled : v => v;
    onRejected = typeof onRejected == 'function' ? onRejected : r => {throw r};

    //2.2.2
    if(this.status == FULFILLED) {
        return promise2 = new MyPromise((resolve, reject) => {
            //2.2.4 我们用setTimeout来模拟异步调用(并不能实现微任务和宏任务的执行机制,只是保证异步调用)
            setTimeout(() => {
                try {
                    // 2.2.5 onFulfilled 和 onRejected 作为函数直接调用
                    let x = onFulfilled(this.value);
                    resolvePromise(promise2, x, resolve, reject)
                } catch (e){
                    reject(e)
                }
            })
        })
    }

    //2.2.3
    if(this.status == REJECTED) {
        return promise2 = new MyPromise((resolve, reject) => {
            setTimeout(() => {
                try {
                    let x = onRejected(this.reason);
                    resolvePromise(promise2, x, resolve, reject)
                } catch (e){
                    reject(e)
                }
            })
        })
    }

    if(this.status == PENDING) {
        return promise2 = new MyPromise((resolve, reject) => {
            // 将fulfilled回调暂存
            this.resolveCallbacks.push((v) => {
                setTimeout(() => {
                    try {
                        let x = onFulfilled(v);
                        resolvePromise(promise2, x, resolve, reject)
                    } catch (e){
                        reject(e)
                    }
                })
            })

            // 将rejected回调暂存
            this.rejectCallbacks.push((r) => {
                setTimeout(() => {
                    try {
                        let x = onRejected(r);
                        resolvePromise(promise2, x, resolve, reject)
                    } catch (e){
                        reject(e)
                    }
                })
            })
        })
    }
}

/**
 * 规范2.3.
 * 如果x为Promise对象,则promise2接收x状态
 * 如果 resolvePromise 和 rejectPromise 均被调用,或者被同一参数调用了多次,
 * 则优先采用首次调用并忽略剩下的调用
 * 
 * @param {*} promise2 : new MyPromise()
 * @param {*} x : new MyPromise()
 * @param {*} resolve : promise2的resolve
 * @param {*} reject : promise2的reject
 * 
 */
function resolvePromise(promise2, x, resolve, reject) {
    // 2.3.1
    if(promise2 == x) {
        reject(new TypeError('error 🐯'))
    }

    //2.3.2
    if(x instanceof MyPromise) {
        x.then(y => {
            resolvePromise(promise2, y, resolve, reject)
        }, r => {
            reject(r)
        })
    } else {
        // 2.3.3
        if(x && ['object', 'function'].includes(typeof x)) {
            let used = false;
            let then;
            try {
                then = x.then;
            } catch (e) {
                if(used) return;
                used = true;
                reject(e)
            }
            if(typeof then == 'function') {
                try {
                    then.call(x, y => {
                        if(used) return;
                        used = true;
                        resolvePromise(promise2, y, resolve, reject)
                    }, r => {
                        if(used) return;
                        used = true;
                        reject(r)
                    })
                } catch (e) {
                    if(used) return;
                    used = true;
                    reject(e);
                }
                
            } else {
                if(used) return;
                used = true;
                resolve(x);
            }
        } else {
            // 2.3.4
            resolve(x);
        }
    }
}

// 使用promises-aplus-tests 测试时需要
// MyPromise.defer = MyPromise.deferred = function () {
//     let dfd = {};
//     dfd.promise = new MyPromise((resolve, reject) => {
//         dfd.resolve = resolve;
//         dfd.reject = reject;
//     });
//     return dfd;
// }

// module.exports = MyPromise;

有开源的测试工具通872个测试用专门来测试是否符合 Promise/A+ 规范 promises-aplus-tests;

可以通过一下命令安装

npm install -g promises-aplus-tests

然后在编写的js文件中加入如下代码:

MyPromise.defer = MyPromise.deferred = function () {
    let dfd = {};
    dfd.promise = new MyPromise((resolve, reject) => {
        dfd.resolve = resolve;
        dfd.reject = reject;
    });
    return dfd;
}

module.exports = MyPromise;

执行:

promises-aplus-tests promise.js