手写 Promise/A+ 规范

手写 Promise/A+ 规范

所谓 Promise,简单说就是一个容器,里面保存着某个未来才会结束的事件(通常是一个异步操作)的结果。从语法上说,Promise 是一个对象,从它可以获取异步操作的消息。Promise 提供统一的 API,各种异步操作都可以用同样的方法进行处理。

想要写出 Promise/A+ 规范的 promsie,首先要熟读 Promise/A+ 的英文规范文档,建议先看几遍英文文档,了解一下规则。还有这篇中文文档翻译的也很到位 中文规范文档

状态:

Promise存在三个状态(state)pending、fulfilled、rejected

pending(等待态)为初始态,并可以转化为 fulfilled(成功态)和 rejected(失败态),一旦确定就不可以改变

如果 executor 函数报错,则直接执行 reject(reason);

then 方法:

promise的 then 方法接收两个可选参数,表示该 promise 状态改变时的回调

then方法返回一个promisethen 方法可以被同一个 promise 调用多次

promise2 = promise1.then(onFulfilled,onRejected)

resolvePromise 方法:

这个方法是用来处理 then 方法中的 onFulfilledonRejected 这两个参数,当这两个参数是函数的时候,处理它们的返回值

下面就根据日常对 promise 的使用规则出发去写一下 Promise/A+ 规范吧。

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

class Promise {
    constructor(executor) {
        this.status = PENDING;
        this.value = undefined;
        this.reason = undefined;


        // 同一个 promsie 实例可能会被多次调用
        this.onResolvedCallbacks = [];
        this.onRejectedCallbacks = [];

        let resolve = value => {
            if (this.status === PENDING) {
                this.status = FULFILLED;
                this.value = value;
              
                this.onResolvedCallbacks.forEach(fn => fn());
            }
        }

        let reject = reason => {
            if (this.status === PENDING) {
                this.status = REJECTED;
                this.reason = reason;

                this.onRejectedCallbacks.forEach(fn => fn());
            }
        }

        try {
            executor(resolve, reject);
        } catch (error) {
            reject(error);
        }
    }

    then(onFulfilled, onRejected) {
        // 解决 onFufilled,onRejected 没有传值的问题
        onFulfilled = typeof onFulfilled === 'function' ? onFulfilled : v => v;
        // 因为错误的值要让后面访问到,所以这里也要抛出错误,不然会在之后 then 的 resolve 中捕获
        onRejected = typeof onRejected === 'function' ? onRejected : err => { throw err };
        // 每次调用 then 都返回一个新的 promise
        let promise2 = new Promise((resolve, reject) => {
            if (this.status === FULFILLED) {
                //Promise/A+ 2.2.4 --- setTimeout
                // 在执行上下文栈中只包含平台代码之前,onFulfilled或onRejected一定不能被调用 [3.1]
                setTimeout(() => {
                    try {
                        let x = onFulfilled(this.value);
                        // x可能是一个proimise
                        resolvePromise(promise2, x, resolve, reject);
                    } catch (error) {
                        reject(error);
                    }
                }, 0);
            }

            if (this.status === REJECTED) {
                setTimeout(() => {
                    try {
                        let x = onRejected(this.reason);
                        resolvePromise(promise2, x, resolve, reject);
                    } catch (error) {
                        reject(error);
                    }
                }, 0);
            }

            if (this.status === PENDING) {
                this.onResolvedCallbacks.push(() => {
                    setTimeout(() => {
                        try {
                            let x = onFulfilled(this.value);
                            resolvePromise(promise2, x, resolve, reject);
                        } catch (error) {
                            reject(error);
                        }
                    }, 0);
                })

                this.onRejectedCallbacks.push(() => {
                    setTimeout(() => {
                        try {
                            let x = onRejected(this.reason);
                            resolvePromise(promise2, x, resolve, reject);
                        } catch (error) {
                            reject(error);
                        }
                    }, 0);
                })
            }
        })
        // Promise/A+ 2.2.7. then必须返回一个promise [3.3]
        return promise2;
    }
}

const resolvePromise = function (promise2, x, resolve, reject) {
    // 自己等待自己完成是错误的实现,用一个类型错误,结束掉 promise  Promise/A+ 2.3.1

    if (promise2 === x) {
        return reject(new TypeError('err'));
    }
    // Promise/A+ 2.3.3.3.3 只能调用一次
    let called;

    if ((x && typeof x === 'object') || typeof x === 'function') {
        try {
            let then = x.then;
            if (typeof then === 'function') {
                // 不要写成 x.then,直接 then.call 就可以了 因为 x.then 会再次取值,防止 Object.defineProperty 中设置的 get 只能访问一次的情况  Promise/A+ 2.3.3.3
                then.call(x,
                    value => {
                        // 根据 promise 的状态决定是成功还是失败
                        if (called) return;
                        called = true;
                        // 递归解析的过程(因为可能 promise 中还有 promise) Promise/A+ 2.3.3.3.1

                        resolvePromise(promise2, value, resolve, reject);
                    },
                    err => {
                        // 只要失败就失败 Promise/A+ 2.3.3.3.2
                        if (called) return;
                        called = true;
                        reject(err);
                    }
                )
            } else {
                // 如果 x.then 是个普通值就直接返回 resolve 作为结果  Promise/A+ 2.3.3.4
                resolve(x);
            }
        } catch (error) {
            // Promise/A+ 2.3.3.2
            if (called) return;
            called = true;
            reject(error);
        }
    } else {
        // 如果 x 是个普通值就直接返回 resolve 作为结果  Promise/A+ 2.3.4
        resolve(x);
    }
}

Promise 写完之后可以通过 promises-aplus-tests 这个包对我们写的代码进行测试,看是否符合 A+ 规范。不过测试前还得加一段代码:

// promise.js
// 这里是上面写的 Promise 全部代码
Promise.defer = Promise.deferred = function () {
    let dfd = {}
    dfd.promise = new Promise((resolve, reject) => {
        dfd.resolve = resolve;
        dfd.reject = reject;
    });
    return dfd;
}
module.exports = Promise;

全局安装:

npm i promises-aplus-tests -g

终端下执行验证命令:

promises-aplus-tests promise.js

结语:刚开始接触 promise 的时候,觉得这东西好抽象,但是用了段时间之后,感觉确实好用。作为一名有追求的程序员,怎么能不挑战一下 promise/A+ 呢!不能被它的规范很多吓到,其实它的规范也是我们平时使用的规则,从这个点出发去写,就不那么枯燥了嘿嘿

------------- 本文结束 感谢阅读 -------------