跟着 Promise A+ 写 Promise

19 阅读2分钟

前言

在工作中每天都在写 Promise,感觉自己对 Promise 很了解,今天看了一下 Promise 规范才发现有一些很基本的细节一直被我忽略了。今天就让我们跟着Promise A+规范来重新学习一下 Promise。

规范

手写 Promise 之前我们首先要了解我们需要实现哪些功能。

完整版参考:Promise/A+

MDN:Promise

executor 函数

功能点

  1. Promise 的状态只能是这三种之一: pending, fulfilled, 或者 rejected。
  2. 当状态是 pending 可以变成 fulfilled 或者 rejected。
  3. 当状态是 fulfilled 时,状态不能被改变,并且要有一个 value。
  4. 当状态是 rejected 时,状态不能被改变,并且要有一个 reason。
  5. executor 函数需要被立即执行。举个栗子
new Promise((resolve, reject) => { 
    // 这里的代码需要立即执行
    setTimeout(() => { resolve("foo"); }, 300); 
});

实现

class SzzPromise {
    constructor(executor) {
        this.state = 'pending';
        this.value = undefined;
        this.reason = undefined;
        const resolve = (v) => {
            if (this.state === 'pending') {
                this.state = 'fulfilled';
                this.value = v;
            }
        };
        const reject = (v) => {
            if (this.state === 'pending') {
                this.state = 'rejected';
                this.reason = v;
            }
        };
        executor(resolve, reject);
    }
}

then 函数

功能点

  1. then 函数有两个参数,这两个参数都是可选的,我们先假设这两个参数叫做 onFulfilled 和 onRejected。
  2. onFulfilled 和 onRejected 都是一个函数。
  3. onFulfilled 会在 Promise 状态变为 fulfilled 之后执行,并且只能被执行一次。
  4. onRejected 会在 Promise 状态变为 fulfilled 之后执行,并且只能被执行一次。
  5. 同一个 Promise 的 then 可以被调用多次,举个例子
const p1 = new Promise((resolve, reject) => { 
    // 这里的代码需要立即执行 
    setTimeout(() => { resolve("foo"); }, 300); 
});
// 同一个 Promise 的 then 可以被调用多次
p1.then(console.log);
p1.then(console.log);
p1.then(console.log);

实现

class SzzPromise {
    constructor(executor) {
        this.state = 'pending';
        this.onFulfilledCallbacks = [];
        this.onRejectedCallbacks = [];
        this.value = undefined;
        this.reason = undefined;
        const resolve = (v) => {
            if (this.state === 'pending') {
                this.state = 'fulfilled';
                this.value = v;
                this.onFulfilledCallbacks.forEach((cb) => cb(v));
            }
        };
        const reject = (v) => {
            if (this.state === 'pending') {
                this.state = 'rejected';
                this.reason = v;
                this.onRejectedCallbacks.forEach((cb) => cb(v));
            }
        };
        executor(resolve, reject);
    }
    then(onFulfilled, onRejected) {
        this.onFulfilledCallbacks.push(onFulfilled);
        this.onRejectedCallbacks.push(onRejected);
    }
}
  1. then 必须返回一个 Promise
function resolveWithTryCatch(fn, value, resolve, reject) {
    try {
        const result = fn(value);
        resolve(result);
    } catch (e) {
        reject(e);
    }
}
class SzzPromise {
    constructor(executor) {
        this.state = 'pending';
        this.onFulfilledCallbacks = [];
        this.onRejectedCallbacks = [];
        this.value = undefined;
        this.reason = undefined;
        const resolve = (v) => {
            if (this.state === 'pending') {
                this.state = 'fulfilled';
                this.value = v;
                this.onFulfilledCallbacks.forEach((cb) => cb(v));
            }
        };
        const reject = (v) => {
            if (this.state === 'pending') {
                this.state = 'rejected';
                this.reason = v;
                this.onRejectedCallbacks.forEach((cb) => cb(v));
            }
        };
        executor(resolve, reject);
    }

    then(onFulfilled, onRejected) {
        const fulfilledFn = typeof onFulfilled === 'function' ? onFulfilled : (v) => v;
        const rejectedFn = typeof onRejected === 'function' ? onRejected : (v) => { throw v; };
        return new SzzPromise((resolve, reject) => {
            if (this.state === 'fulfilled') {
                resolveWithTryCatch(fulfilledFn, this.value, resolve, reject);
            } else if (this.state === 'rejected') {
                resolveWithTryCatch(rejectedFn, this.reason, resolve, reject);
            } else if (this.state === 'pending') {
                this.onFulfilledCallbacks.push((v) => {
                    resolveWithTryCatch(fulfilledFn, v, resolve, reject);
                });
                this.onRejectedCallbacks.push((v) => {
                    resolveWithTryCatch(rejectedFn, v, resolve, reject);
                });
            }
        });
    }
}

结语

以上就是我们的所有内容,我们只实现了Promise的主体功能,还有很多细节需要优化。 如果想要知道如何写出能够100%通过Promise单元测试的实现可以参考这篇文章