简单手写promise

77 阅读3分钟

写过好几次promise,但是感觉还是没有完全掌握,今天又写了一遍,理下思路:

一、promise类

  1. 写一个promise类,包含以下状态:
    • status
    • value
    • reason
    • resolveQueue
    • rejectQueue
  2. resolve的操作
    • 更新状态为fulfilled
    • 设置value的值
    • 调用resolveQueue队列
  3. reject的操作
    • 更新状态为reject
    • 设置reason的值
    • 调用rejectQueue队列
  4. then:最重要
    • 值透传:onFulfilled、onReject不是必传的,如果没有传要写一个函数,resolve就是直接返回参数,reject是throw错误
    • 返回一个新创建的promise,内部判断三种状态,如果是pending,则push进两个队列,resolve则执行fulfilled队列,reject则执行reject队列
    • 具体的执行函数:执行onFulfilled或者onReject函数得到返回值,判断它的返回值和promise2的关系,进入resolvePromise的流程

/**
 * 1. 包含状态值、value、reason、完成队列函数、拒绝队列函数
 * 2. resolve的操作:更新状态为fulfilled,调用成功函数队列
 * 3. reject的操作:更新状态为reject,调用失败队列
 * 4. then
 *      1. 值透传
 *      2. 返回一个新创建的promise,内部判断状态,如果是pending,则push进去,resolve则执行fulfilled队列,reject则执行reject队列
 *      3. 具体的执行函数,执行onFulfilled函数,判断它的返回值和promise2的关系,进入resolvePromise的流程
 */
class MyPromise2 {
    static PENDING = 'pending';
    static FULFILLED = 'fulfilled';
    static REJECTED = 'reject';
    constructor(executor) {
        this.status = MyPromise2.PENDING;
        this.resolveQueue = [];
        this.rejectQueue = [];
        this.value = null;
        this.reason = null;
        if (typeof executor !== 'function') {
            throw new TypeError('Promise xx');
        }
        try {
            executor(this.resolve.bind(this), this.reject.bind(this));
        } catch(err) {
            this.reject();
        }
    }
    resolve(value) {
        if (this.status !== MyPromise2.PENDING) {
            return;
        }
        this.value = value;
        this.status = MyPromise2.FULFILLED;
        this.resolveQueue.forEach(item => {
            item(this.value);
        });
    }
    reject(reason) {
        if (this.status !== MyPromise2.PENDING) {
            return;
        }
        this.reason = reason;
        this.status = MyPromise2.REJECTED;
        this.rejectQueue.forEach(item => {
            item(this.reason);
        });
    }
    // 如果pending,则push进去,否则执行
    then(onFulfilled, onRejected) {
        onFulfilled = typeof onFulfilled === 'function' ? onFulfilled : value => value;
        onRejected = typeof onRejected === 'function' ? onRejected : reason => {throw reason};

        const promise2 = new MyPromise2((resolve, reject) => {
            if (this.status === MyPromise2.PENDING) {
                this.resolveQueue.push(() => {
                    setTimeout(() => {
                        try {
                            const x = onFulfilled(this.value);
                            resolvePromise(promise2, x, resolve, reject);
                        } catch (err) {
                            reject(err);
                        }
                    });
                });
                this.rejectQueue.push(() => {
                    setTimeout(() => {
                        try {
                            const x = onRejected(this.reason);
                            resolvePromise(promise2, x, resolve, reject);
                        } catch (err) {
                            reject(err);
                        }
                    });
                });
            } else if (this.status === MyPromise2.FULFILLED) {
                setTimeout(() => {
                    try {
                        const x = onFulfilled(this.value);
                        resolvePromise(promise2, x, resolve, reject);
                    } catch (err) {
                        reject(err);
                    }
                });
            } else if (this.status === MyPromise2.REJECTED) {
                setTimeout(() => {
                    try {
                        const x = onRejected(this.reason);
                        resolvePromise(promise2, x, resolve, reject);
                    } catch (err) {
                        reject(err);
                    }
                });
            }
        });
        return promise2;
    }
}

二、resolvePromise函数

2.2.7 then方法必须返回一个promise。[3.3]

promise2 = promise1.then(onFulfilled, onRejected);

2.2.7.1. 如果onFulfilledonRejected返回一个值x,则运行Promise Resolution Procedure [[Resolve]](promise2, x)。 2.2.7.2. 如果onFulfilledonRejected抛出异常e,则promise2必须以e作为原因被拒绝。

下面resolvePromise的决议过程:

  1. 判断是否同一个promise,是的话直接抛出异常

  2. 如果x不为null,是对象或者函数

    • 取出x.then
      • 如果x.then是函数,则调用x.then, x作为context,传入resolve,reject函数,resolve时继续调用resolvePromise来对y和promise2做决议,直到y不是promise,就会走resolve流程了
      • 不是函数,则直接resolve(x)
  3. 否则,直接resolve(x)

 
/**
 * @param {*} promise2 
 * @param {*} x 
 * @param {*} resolve 
 * @param {*} reject 
 * @returns 
 */
function resolvePromise(promise2, x, resolve, reject) {
    // 2.3.1 如果`promise`和`x`引用同一个对象,则以`TypeError`为原因拒绝`promise`
    if (x === promise2) {
        return reject(new TypeError('Error'));
    }
    let called;
    if (x !== null && (typeof x === 'object' || typeof x === 'function')) {
        try {
            const then = x.then;
            if (typeof then === 'function') {
                then.call(x, y => {
                    if (called) {
                        return;
                    }
                    called = true;
                    // 2.3.3.3.1 如果/当`resolvePromise`被调用并传入值`y`,运行`[[Resolve]](promise, y)`。
                    resolvePromise(promise2, y, resolve, reject)
                }, err => {
                    if (called) {
                        return;
                    }
                    called = true;
                    // 2.3.3.2 如果/当`rejectPromise`被调用并传入原因`r`,以`r`拒绝`promise`
                    reject(err);
                })
            } else {
                resolve(x);
            }
        } catch (e) {
            // 2.3.3.2 如果获取属性`x .then`导致抛出异常`e`,则以`e`为原因拒绝`promise`
            if (called) {
                return;
            }
            called = true;
            reject(e);
        }
    } else {
        // 2.3.3.4 如果`then`不是一个函数,则以`x`来实现`promise`
        // 2.3.4 如果`x`不是对象或函数,则用`x`来实现`promise`
        resolve(x);
    }
}

测试代码:

// 自测promise
const promise = new MyPromise2((resolve, reject) => {
    setTimeout(() => {
        resolve('成功');
    },1000);
}).then(
    (data) => {
        console.log('success', data)
    },
    (err) => {
        console.log('faild', err)
    }
)

全部代码已上传github

三、运行测试用例

通过(promises-aplus-tests)可以帮助我们测试所编写的代码是否符合 Promise/A+ 的规范。需要安装npm包。同时运行测试需要添加如下代码:

// 执行promises-aplus-tests测试用例需要用到的代码
MyPromise2.deferred = function() {
    let defer = {};
    defer.promise = new MyPromise2((resolve, reject) => {
        defer.resolve = resolve;
        defer.reject = reject;
    });
    return defer;
  }

安装测试脚本

npm i -g promises-aplus-tests

开始测试

promises-aplus-tests MyPromise.js

image.png