# 前言

嗯嗯嗯,等写完这个博客我就去复习期末。

# 实现过程

# 基本属性

对一个 promise 来说,我们需要有下面这些东西

  1. 对应三个状态的表示
  2. 当前 promise 的数据(也就是成功获得的数据或者失败的原因)
  3. 当前 promise 的状态
  4. 成功时要执行的回调函数列表
  5. 失败时要执行的回调函数列表

另外,为了使用属性私有化,我们要用一个立即执行函数来生成 class,然后私有属性全部使用 Symbol 类型定义

const MyPromise = (function () {
    // 对应的三个状态
    const PENDING = "pending";
    const RESOLVED = "resolved";
    const REJECTED = "rejected";
    // 当前 promise 的数据
    const PromiseValue = Symbol("PromiseValue");
    // 当前 promise 的状态
    const PromiseStatus = Symbol("PromiseStatus");
    // 成功时要执行的回调函数列表
    const onFulfilledList = Symbol("onFulfilledList");
    // 失败时要执行的回调函数列表
    const onRejectedList = Symbol("onRejectedList");
    return class  {
        constructor(executor) {
            this[PromiseStatus] = PENDING;
            this[PromiseValue] = undefined;
            this[onFulfilledList] = [];
            this[onRejectedList] = [];
        }
    }
})();

可以看到我们把状态,数据,两个回调列表存到了 promise 上,然后我们就可以逐步实现一个 promise 了

# 完成基础版本

先看看 promise 最基础的用法

let promise = new Promise((resolve, reject) => {
    resolve("sena");
});

我们可以看到,在 new Promise 时,会传入一个函数,这个函数会立即执行,这个函数接收两个参数,分别是 resolve 和 reject,这两个函数是定义好的,这个函数做了下面几件事

  1. 改变当前 promise 的状态
  2. 把传入的参数作为当前 promise 的数据保存起来
  3. 执行对应回调列表里里的回调函数

另外要注意的是,promise 的状态是不可逆的,只能从 pending 变成 resolved 或者 rejected,不能倒回去,所以我们要先做个判断再改变状态。

因为修改状态为成功和失败的逻辑都是一致的,所以我们可以把逻辑抽象为一个私有的工具函数

const MyPromise = (function () {
   // 上面已经有的重复代码
   ......
   // 更新状态的函数
   const updateStatus = Symbol("updateStatus");
   return class  {
    /**
    * 更新状态
    * @param newStatus 新的状态
    * @param newValue 新的 promise 数据
    * @param executeQueue 要执行的回调列表
    */
    [updateStatus] (newStatus, newValue, executeQueue) {
        // 如果状态不是 pending, 就返回
        if (this[PromiseStatus] !== PENDING) return;
        // 把当前 promise 的状态改成新的状态
        this[PromiseStatus] = newStatus;
        // 把当前 promise 的值更新为新的值
        this[PromiseValue] = newValue;
        // 把回调列表里的函数取出来依次执行
        executeQueue.forEach((handler) => {
            // 把值传给回调函数执行
            handler(newValue);
        })
    }
    constructor(executor) {
        .....
    }
}
})();

好了,我们现在已经定义了一个工具函数 updateStatus ,但这个工具函数还有一点不好的地方,那就是 then 绑定的回调函数都是放到微任务中异步执行的,而我们这里是同步执行,在浏览器环境下,没有办法把回调函数放到微任务中,只能放到宏任务队列中异步执行,所以我们还要再更新一下 updateStatus 函数

因为把一个回调函数推入宏任务队列也可以抽象一下,所以我们写一个把回调函数推入任务队列的工具函数

const MyPromise = (function () {
    ......
    // 更新状态的函数
    const updateStatus = Symbol("updateStatus");
    // 把任务添加到宏任务队列里的函数
    const executeAsync = Symbol("executeAsync");
    return class  {
        /**
         * 把任务推入宏队列
         * @param handler 回调函数
         * @param arg 传递给回调函数的参数
         */
        [executeAsync] (handler, ...arg) {
            setTimeout(function () {
                handler(...arg);
            }, 0)
        }
        /**
         * 更新状态
         * @param newStatus 新的状态
         * @param newValue promise 新的数据
         * @param executeQueue 要执行的回调列表
         */
        [updateStatus] (newStatus, newValue, executeQueue) {
            ......
            executeQueue.forEach((handler) => {
                // 异步执行 then 中注册的回调
                this[executeAsync] (handler, newValue);
            })
        }
        constructor(executor) {
            ......
        }
    }
})();

然后我们在构造器中创建 resolve 和 reject 两个函数,传入 executor 即可

constructor(executor) {
    this[PromiseStatus] = PENDING;
    this[PromiseValue] = undefined;
    this[onFulfilledList] = [];
    this[onRejectedList] = [];
    // 定义 resolve 函数
    const resolve = (data) => {
        this[updateStatus](RESOLVED, data, this[onFulfilledList]);
    };
    // 定义 reject 函数
    const reject = (reason) => {
        this[updateStatus](REJECTED, reason, this[onRejectedList]);
    };
    // 同步执行 new Promise 时传入的函数
    executor && executor(resolve, reject);
}

这样使用时就可以接收到 resolve 和 reject 这两个函数,然后调用他们时,就可以改变 promise 的状态,然后就可以执行 then 中注册到回调列表中的函数了。

当然我们还需要做一点小收尾,因为如果传入的 executor 函数报错,会调用这个 promise 的 reject 方法,并把错误信息传入,所以我们需要加一个 try catch

constructor(executor) {
    this[PromiseStatus] = PENDING;
    this[PromiseValue] = undefined;
    this[onFulfilledList] = [];
    this[onRejectedList] = [];
    // 定义 resolve 函数
    const resolve = (data) => {
        this[updateStatus](RESOLVED, data, this[onFulfilledList]);
    };
    // 定义 reject 函数
    const reject = (reason) => {
        this[updateStatus](REJECTED, reason, this[onRejectedList]);
    };
    try {
        // 同步执行 new Promise 时传入的函数
        executor && executor(resolve, reject);
    }catch (e) {
        // 如果捕获到异常就调用 reject
        reject(e);
    }
}

然后看看我们现在已经写好的所有的代码

const MyPromise = (function () {
    // 对应的三个状态
    const PENDING = "pending";
    const RESOLVED = "resolved";
    const REJECTED = "rejected";
    // 当前 promise 的数据
    const PromiseValue = Symbol("PromiseValue");
    // 当前 promise 的状态
    const PromiseStatus = Symbol("PromiseStatus");
    // 成功时要执行的回调函数列表
    const onFulfilledList = Symbol("onFulfilledList");
    // 失败时要执行的回调函数列表
    const onRejectedList = Symbol("onRejectedList");
    // 更新状态的函数
    const updateStatus = Symbol("updateStatus");
    // 把任务添加到宏任务队列里的函数
    const executeAsync = Symbol("executeAsync");
    return class  {
        /**
         * 把任务推入宏队列
         * @param handler 回调函数
         * @param arg 传递给回调函数的参数
         */
        [executeAsync] (handler, ...arg) {
            setTimeout(function () {
                handler(...arg);
            }, 0)
        }
        /**
         * 更新状态
         * @param newStatus 新的状态
         * @param newValue promise 新的数据
         * @param executeQueue 要执行的回调列表
         */
        [updateStatus] (newStatus, newValue, executeQueue) {
            // 如果状态不是 pending, 就返回
            if (this[PromiseStatus] !== PENDING) return;
            // 把当前 promise 的状态改成新的状态
            this[PromiseStatus] = newStatus;
            // 把当前 promise 的值更新为新的值
            this[PromiseValue] = newValue;
            // 把回调列表里的函数取出来依次执行
            executeQueue.forEach((handler) => {
                // 异步执行 then 中注册的回调
                this[executeAsync] (handler, newValue);
            })
        }
        constructor(executor) {
            this[PromiseStatus] = PENDING;
            this[PromiseValue] = undefined;
            this[onFulfilledList] = [];
            this[onRejectedList] = [];
            // 定义 resolve 函数
            const resolve = (data) => {
                this[updateStatus](RESOLVED, data, this[onFulfilledList]);
            };
            // 定义 reject 函数
            const reject = (reason) => {
                this[updateStatus](REJECTED, reason, this[onRejectedList]);
            };
            try {
                // 同步执行 new Promise 时传入的函数
                executor && executor(resolve, reject);
            }catch (e) {
                // 如果捕获到异常就调用 reject
                reject(e);
            }
        }
    }
})();

测试一下

let promise = new MyPromise((resolve, reject) => {
    resolve("sena");
});

自然是没有反应的,因为没有 then,没有注册回调函数,自然什么也不会发生

# 实现 then

实现 then 也不难,根据规范,我们需要返回一个 promise,因为返回的新 promise 和现有的 promise 需要关联,这里我们用一个新的工具函数来创建这个 promise

const MyPromise = (function () {
    ......
    // 创建串联的 promise
    const createLinkPromise = Symbol("createLinkPromise");
    return class  {
        ......
        /**
         * 用于创建 then 返回的新 promise
         * @param onFulfilled then 里传入的 onFulfilled 函数
         * @param onRejected then 里传入的 onRejected 函数
         */
        [createLinkPromise] (onFulfilled, onRejected) {
            return new MyPromise((resolve, reject) => {
            });
        }
        then(onFulfilled, onRejected) {
            return this[createLinkPromise](onFulfilled, onRejected);
        }
        constructor(executor) {
            ......
        }
           
    }
})();

这里我们使用了一个新的工具函数 createLinkPromise ,用于返回一个新的 promise,在这里我们会对 then 的绑定的回调函数进行处理。

# 实现单个 then 的绑定事件

then 如果传入了回调函数,首先会判断当前 promise 的状态,如果已经是 resolve 或者 reject 状态,就把回调函数推入微任务队列中异步执行,如果还是 pending 状态,就把回调函数放到 promise 的回调函数列表中,等状态改变了再执行,大概思路就是这样,我们现在来实现一下。

因为判断状态,立即执行,加入队列都可以抽象,所以我们用一个工具函数来完成这个过程

const MyPromise = (function () {
    ......
    // 处理 then 中传入的回调
    const settleHandle = Symbol("settleHandle");
    return class  {
        
        ......
        /**
         * 处理 then 传入的回调函数
         * @param handler 回调函数
         * @param immediatelyStatus 立即推入任务队列执行的状态
         * @param queue 如果不立即执行,保存的队列
         */
        [settleHandle] (handler, immediatelyStatus, queue) {
            // 如果传入的不是一个函数就返回
            if (typeof handler !== "function") return;
            // 判断是不是立刻执行的状态
            if (this[PromiseStatus] === immediatelyStatus) {
                // 是就立刻推入异步执行队列
                this[executeAsync] (handler, this[PromiseValue]);
            } else {
                // 不是就放到回调列表中
                queue.push(handler);
            }
        }
        /**
         * 用于创建 then 返回的新 promise
         * @param onFulfilled then 里传入的 onFulfilled 函数
         * @param onRejected then 里传入的 onRejected 函数
         */
        [createLinkPromise] (onFulfilled, onRejected) {
            return new MyPromise((resolve, reject) => {
                // 在这里调用 [settleHandle] 就处理 then 传入的回调了
                this[settleHandle](resolve, RESOLVED, this[onFulfilledList]);
                this[settleHandle](reject, REJECTED, this[onRejectedList]);
            });
        }
        then(onFulfilled, onRejected) {
            return this[createLinkPromise](onFulfilled, onRejected);
        }
        
        constructor(executor) {
            ......
        }
    }
})();

settleHandle 就是用来处理回调函数的工具函数。

好了,现在我们可以给 promise 通过 then 注册回调了,来看看完整的代码吧

const MyPromise = (function () {
    // 对应的三个状态
    const PENDING = "pending";
    const RESOLVED = "resolved";
    const REJECTED = "rejected";
    // 当前 promise 的数据
    const PromiseValue = Symbol("PromiseValue");
    // 当前 promise 的状态
    const PromiseStatus = Symbol("PromiseStatus");
    // 成功时要执行的回调函数列表
    const onFulfilledList = Symbol("onFulfilledList");
    // 失败时要执行的回调函数列表
    const onRejectedList = Symbol("onRejectedList");
    // 更新状态的函数
    const updateStatus = Symbol("updateStatus");
    // 把回调函数添加到任务队列里异步执行的函数
    const executeAsync = Symbol("executeAsync");
    // 创建串联的 promise
    const createLinkPromise = Symbol("createLinkPromise");
    // 后续处理的通用函数
    const settleHandle = Symbol("settleHandle");
    return class  {
        /**
         * 让回调函数异步执行
         * @param handler 回调函数
         * @param arg 传递给回调函数的参数
         */
        [executeAsync] (handler, ...arg) {
            setTimeout(function () {
                handler(...arg);
            }, 0)
        }
        /**
         * 更新状态
         * @param newStatus 新的状态
         * @param newValue promise 新的数据
         * @param executeQueue 要执行的回调列表
         */
        [updateStatus] (newStatus, newValue, executeQueue) {
            // 如果状态不是 pending, 就返回
            if (this[PromiseStatus] !== PENDING) return;
            // 把当前 promise 的状态改成新的状态
            this[PromiseStatus] = newStatus;
            // 把当前 promise 的值更新为新的值
            this[PromiseValue] = newValue;
            // 把回调列表里的函数取出来依次执行
            executeQueue.forEach((handler) => {
                // 异步执行 then 中注册的回调
                this[executeAsync] (handler, newValue);
            })
        }
        /**
         * 处理 then 传入的回调函数
         * @param handler 回调函数
         * @param immediatelyStatus 立即推入任务队列执行的状态
         * @param queue 如果不立即执行,保存的队列
         */
        [settleHandle] (handler, immediatelyStatus, queue) {
            // 如果传入的不是一个函数就返回
            if (typeof handler !== "function") return;
            // 判断是不是立刻执行的状态
            if (this[PromiseStatus] === immediatelyStatus) {
                // 是就立刻推入异步执行队列
                this[executeAsync] (handler, this[PromiseValue]);
            } else {
                // 不是就放到回调列表中
                queue.push(handler);
            }
        }
        /**
         * 用于创建 then 返回的新 promise
         * @param onFulfilled then 里传入的 onFulfilled 函数
         * @param onRejected then 里传入的 onRejected 函数
         */
        [createLinkPromise] (onFulfilled, onRejected) {
            return new MyPromise((resolve, reject) => {
                // 在这里调用 [settleHandle] 就处理 then 传入的回调了
                this[settleHandle](onFulfilled, RESOLVED, this[onFulfilledList]);
                this[settleHandle](onRejected, REJECTED, this[onRejectedList]);
            });
        }
        then(onFulfilled, onRejected) {
            return this[createLinkPromise](onFulfilled, onRejected);
        }
        constructor(executor) {
            this[PromiseStatus] = PENDING;
            this[PromiseValue] = undefined;
            this[onFulfilledList] = [];
            this[onRejectedList] = [];
            // 定义 resolve 函数
            const resolve = (data) => {
                this[updateStatus](RESOLVED, data, this[onFulfilledList]);
            };
            // 定义 reject 函数
            const reject = (reason) => {
                this[updateStatus](REJECTED, reason, this[onRejectedList]);
            };
            try {
                // 同步执行 new Promise 时传入的函数
                executor && executor(resolve, reject);
            }catch (e) {
                // 如果捕获到异常就调用 reject
                reject(e);
            }
        }
    }
})();

测试一下

let promise = new MyPromise((resolve, reject) => {
    setTimeout(function () {
        resolve("sena");
    }, 1000);
    console.log("123");       // 输出 123
});
promise.then(function (data) {
    console.log(1, data);    // 1s 后输出 1 "sena"
});
promise.then(function (data) {
    console.log(2, data);    // 1s 后输出 2 "sena"
});
promise.then(function (data) {
    console.log(3, data);    // 1s 后输出 3 "sena"
});

# 实现 then 的链式调用

因为 then 已经返回了一个 promise,所以我们要做的事情比较简单,就是处理一下回调函数的返回值和处理回调函数抛出的 error 就就行了,我们先处理返回值,返回值有两种情况

下文的的下一个 Promise 代指 createLinkPromise 返回的 promise

  1. 返回非 promise,直接把返回值通过调用 resolve() 传给下一个 promise,下一个 promise 把这个返回值传给 then 注册的回调函数,就实现了前一个 then 的返回值作为下个 then 的参数
  2. 返回 promise,给返回的 promise 用 then 注册成功和失败的回调,在两个回调中分别调用能修改下一个 promise 的状态的 resolve 和 reject 方法,从而完成数据的传递和延时执行

我们看看之前写的代码

[createLinkPromise] (onFulfilled, onRejected) {
    return new MyPromise((resolve, reject) => {
        // 在这里调用 [settleHandle] 就处理 then 传入的回调了
        this[settleHandle](onFulfilled, RESOLVED, this[onFulfilledList]);
        this[settleHandle](onRejected, REJECTED, this[onRejectedList]);
    });
}
[settleHandle] (handler, immediatelyStatus, queue) {
    // 如果传入的不是一个函数就返回
    if (typeof handler !== "function") return;
    // 判断是不是立刻执行的状态
    if (this[PromiseStatus] === immediatelyStatus) {
        // 是就立刻推入异步执行队列
        this[executeAsync] (handler, this[PromiseValue]);
    } else {
        // 不是就放到回调列表中
        queue.push(handler);
    }
}

很明显,因为我们不知道当前 promise 的状态,所以我们不知道传入的回调函数声明时候执行,所以要接收返回值,就要做一些特殊处理。

处理的方法也很简单,在回调函数的外面包一层,在外层函数里接收回调函数的返回值就行了

[createLinkPromise] (onFulfilled, onRejected) {
    return new MyPromise((resolve, reject) => {
        // 在这里调用 [settleHandle] 就能处理 then 传入的回调了
        // 在外面套一层 
        this[settleHandle]((data) => {
            // 在这里就可以接收返回值,对返回值进行处理
            let result = onFulfilled(data);
            // 把返回值传给下个 promise
            resolve(result);
        }, RESOLVED, this[onFulfilledList]);
        this[settleHandle]((err) => {
            let result = onRejected(err);
            resolve(result);
        }, REJECTED, this[onRejectedList]);
    });
}

根据规范,如果 then 传入的 onFulfilled 函数执行时抛出了错误,就会调用 reject 函数,所以我们加个 try catch 在外面,由因为加 try catch 是重复的逻辑,所以我们可以再抽成一个工具函数 execute 。修改后的代码是这样的

const MyPromise = (function () {
    ......
    // 执行 then 绑定的回调,同时 try catch
    const execute = Symbol("execute");
    return class  {
        ......
        /**
         * 执行回调,获取返回值,如果报错就执行 reject
         * @param data 传给 handler 的数据
         * @param handler 要在 try catch 中执行的回调函数
         * @param resolve 下个 promise 的 resolve
         * @param reject 下个 promise 的 reject
         */
        [execute] (data, handler, resolve, reject) {
            try {
                const result = handler(data);
                resolve(result);
            }catch (e) {
                reject(e);
            }
        }
        /**
         * 用于创建 then 返回的新 promise
         * @param onFulfilled then 里传入的 onFulfilled 函数
         * @param onRejected then 里传入的 onRejected 函数
         */
        [createLinkPromise] (onFulfilled, onRejected) {
            return new MyPromise((resolve, reject) => {
                // 在这里调用 [settleHandle] 就处理 then 传入的回调了
                this[settleHandle]((data) => {
                    this[execute](data, onFulfilled, resolve, reject);
                }, RESOLVED, this[onFulfilledList]);
                this[settleHandle]((err) => {
                    this[execute](err, onRejected, resolve, reject);
                }, REJECTED, this[onRejectedList]);
            });
        }
        
        constructor(executor) {
            ......
        }
    }
})();

现在的代码是这样的

const MyPromise = (function () {
    // 对应的三个状态
    const PENDING = "pending";
    const RESOLVED = "resolved";
    const REJECTED = "rejected";
    // 当前 promise 的数据
    const PromiseValue = Symbol("PromiseValue");
    // 当前 promise 的状态
    const PromiseStatus = Symbol("PromiseStatus");
    // 成功时要执行的回调函数列表
    const onFulfilledList = Symbol("onFulfilledList");
    // 失败时要执行的回调函数列表
    const onRejectedList = Symbol("onRejectedList");
    // 更新状态的函数
    const updateStatus = Symbol("updateStatus");
    // 把任务添加到任务队列里异步执行的函数
    const executeAsync = Symbol("executeAsync");
    // 创建串联的 promise
    const createLinkPromise = Symbol("createLinkPromise");
    // 后续处理的通用函数
    const settleHandle = Symbol("settleHandle");
    // 执行 then 绑定的回调,同时 try catch
    const execute = Symbol("execute");
    return class  {
        /**
         * 异步执行回调函数
         * @param handler 回调函数
         * @param arg 传递给回调函数的参数
         */
        [executeAsync] (handler, ...arg) {
            setTimeout(function () {
                handler(...arg);
            }, 0)
        }
        /**
         * 更新状态
         * @param newStatus 新的状态
         * @param newValue promise 新的数据
         * @param executeQueue 要执行的回调列表
         */
        [updateStatus] (newStatus, newValue, executeQueue) {
            // 如果状态不是 pending, 就返回
            if (this[PromiseStatus] !== PENDING) return;
            // 把当前 promise 的状态改成新的状态
            this[PromiseStatus] = newStatus;
            // 把当前 promise 的值更新为新的值
            this[PromiseValue] = newValue;
            // 把回调列表里的函数取出来依次执行
            executeQueue.forEach((handler) => {
                // 异步执行 then 中注册的回调
                this[executeAsync] (handler, newValue);
            })
        }
        /**
         * 处理 then 传入的回调函数
         * @param handler 回调函数
         * @param immediatelyStatus 立即推入任务队列执行的状态
         * @param queue 如果不立即执行,保存的队列
         */
        [settleHandle] (handler, immediatelyStatus, queue) {
            // 如果传入的不是一个函数就返回
            if (typeof handler !== "function") return;
            // 判断是不是立刻执行的状态
            if (this[PromiseStatus] === immediatelyStatus) {
                // 是就立刻推入异步执行队列
                this[executeAsync] (handler, this[PromiseValue]);
            } else {
                // 不是就放到回调列表中
                queue.push(handler);
            }
        }
        /**
         * 执行回调,获取返回值,如果报错就执行 reject
         * @param data 传给 handler 的数据
         * @param handler 要在 try catch 中执行的回调函数
         * @param resolve 下个 promise 的 resolve
         * @param reject 下个 promise 的 reject
         */
        [execute] (data, handler, resolve, reject) {
            try {
                const result = handler(data);
                // 如果返回值是 promise
                if (result instanceof MyPromise) {
                    // 用 then 注册回调
                    result.then((data) => {
                        // 把回调收到的数据转发给下个 promise
                        resolve(data);
                    }, (err) => {
                        reject(err);
                    })
                }else {
                    // 如果返回值不是 promise,直接传递就行
                    resolve(result);
                }
            }catch (e) {
                reject(e);
            }
        }
        /**
         * 用于创建 then 返回的新 promise
         * @param onFulfilled then 里传入的 onFulfilled 函数
         * @param onRejected then 里传入的 onRejected 函数
         */
        [createLinkPromise] (onFulfilled, onRejected) {
            return new MyPromise((resolve, reject) => {
                // 在这里调用 [settleHandle] 就能处理 then 传入的回调了
                this[settleHandle]((data) => {
                    this[execute](data, onFulfilled, resolve, reject);
                }, RESOLVED, this[onFulfilledList]);
                this[settleHandle]((err) => {
                    this[execute](err, onRejected, resolve, reject);
                }, REJECTED, this[onRejectedList]);
            });
        }
        then(onFulfilled, onRejected) {
            return this[createLinkPromise](onFulfilled, onRejected);
        }
        constructor(executor) {
            this[PromiseStatus] = PENDING;
            this[PromiseValue] = undefined;
            this[onFulfilledList] = [];
            this[onRejectedList] = [];
            // 定义 resolve 函数
            const resolve = (data) => {
                this[updateStatus](RESOLVED, data, this[onFulfilledList]);
            };
            // 定义 reject 函数
            const reject = (reason) => {
                this[updateStatus](REJECTED, reason, this[onRejectedList]);
            };
            try {
                // 同步执行 new Promise 时传入的函数
                executor && executor(resolve, reject);
            }catch (e) {
                // 如果捕获到异常就调用 reject
                reject(e);
            }
        }
    }
})();

好了,现在我们已经处理完了返回值不是 promise 的情况,让我们测试一下

let promise = new MyPromise((resolve, reject) => {
    setTimeout(function () {
        resolve(1);
    }, 1000);
}).then(function (data) {
    console.log(data);    // 1s 后输出 1
    return data + 1;
}).then(function (data) {
    console.log(data);    // 1s 后输出 2
    return data + 1;
}).then(function (data) {
    console.log(data);    // 1s 后输出 3
    return data + 1;
});

现在我们来处理返回值是 promise 的情况,处理的方法原理也不难,给返回的 promise 注册回调,然后在回调里修改下一个 promise 的状态就行了

PS:下一个 promise 指的是 createLinkPromise 返回的 promise

这里直接修改 execute 函数就行了

/**
 * 执行回调,获取返回值,如果报错就执行 reject
 * @param data 传给 handler 的数据
 * @param handler 要在 try catch 中执行的回调函数
 * @param resolve 下个 promise 的 resolve
 * @param reject 下个 promise 的 reject
 */
[execute] (data, handler, resolve, reject) {
    try {
        const result = handler(data);
        // 如果返回值是 promise
        if (result instanceof MyPromise) {
            // 用 then 注册回调
            result.then((data) => {
                // 把回调收到的数据转发给下个 promise
                resolve(data);
            }, (err) => {
                reject(err);
            })
        }else {
            // 如果返回值不是 promise,直接传递就行
            resolve(result);
        }
    }catch (e) {
        reject(e);
    }
}

好了,返回值是 promise 的情况也处理好了,来测试一下

let promise = new MyPromise((resolve, reject) => {
    setTimeout(function () {
        resolve("sena");
    }, 1000);
    console.log("sion");
}).then((data) => {
    console.log("1",data);
    return data;
}).then((data) => {
    console.log("2",data);
    return new MyPromise((resolve) => {
        setTimeout(() => {
            resolve("sakura");
        }, 2000)
    });
}).then((data) => {
    console.log("3",data);
    return data;
});
console.log("yyy");

输出情况,看起来是正常的

// sion
// yyy
// 1s 后输出: 1 sena
// 1s 后输出: 2 sena
// 2s 后输出: 3 sakura

其实到这里,一个 promise 大致就完成了,接下来就是一些细节处理了

# 处理空 then

如果中间的某个 then 没有传入回调,虽然我没在规范中找到这种情况(可能是我看漏了),但是在我测试官方 promise 时回调函数的执行不会中断,所以我们要做些处理

处理的方法也比较简单,如果 onFulfilled, onRejected 没有传入,使用默认的 onFulfilled, onRejected 函数,把数据转发就行了

const MyPromise = (function () {
    ......
    // 默认的 OnFulfilled
    const defaultOnFulfilled = function (data) {return data};
    // 默认的 OnRejected
    const defaultOnRejected = function (err) {throw new Error(err)};
    return class  {
        // 给 onFulfilled, onRejected 默认值
        then(onFulfilled = defaultOnFulfilled, onRejected = defaultOnRejected) {
            return this[createLinkPromise](onFulfilled, onRejected);
        }
        constructor(executor) {
            ......
        }
    }
})();

测试代码

let promise = new MyPromise((resolve, reject) => {
    setTimeout(function () {
        resolve("sena");
    }, 1000);
    console.log("sion");
}).then((data) => {
    console.log("1",data);
    return data;
}).then((data) => {
    console.log("2",data);
    return new MyPromise((resolve) => {
        setTimeout(() => {
            resolve("sakura");
        }, 1000)
    });
}).then().then((data) => {
    console.log("3",data);
    return data;
});
console.log("yyy");

输出情况

// sion
// yyy
// 1s 后输出: 1 sena
// 1s 后输出: 2 sena
// 2s 后输出: 3 sakura      如果没有处理空 then,不会出现这个

# 完善 promise 的其他函数

# catch 和 finally 函数

catch 其实是特殊的 then 函数,简单来说就是没有 onFulfilled 的 then 函数。

finally 也是特殊的 then 函数,就是无论什么状态都执行相同的回调

所以可以直接调用 then 来实现

catch(onRejected) {
    return this.then(defaultOnFulfilled, onRejected);
}
finally(handler) {
    return this.then(handler, handler);
}

# all 函数

all 函数接收一个 promise 数组,返回一个 promise,只有传入的数据中的所有 promise 都处于 resolved 状态后,返回的 promise 才会处于 resolved 状态,有任何一个 promise 处于 reject 状态后,返回的 promise 就会处于 reject 状态

static all(promises) {
    //promise 数组的长度
    let length = promises.length;
    // 创建用于保存结果的数组
    let resultArr = new Array(length);
    // 已经处于 resolved 状态的 promise 的个数
    let count = 0;
    return new MyPromise((resolve, reject) => {
        promises.map((promise, index) => {
            // 给每个 promise 都注册回调
            promise.then((data) => {
                // 处于 resolve 状态后,把 count 加 1
                count ++;
                // 保存数据到数组中
                resultArr[index] = data;
                // 如果全部处于 resolved 状态
                if (count >= length) {
                    // 就把返回的 promise 变成 resolved 状态
                    resolve(resultArr);
                }
            }, (err) => {
                // 有任一个处于 rejected 状态
                // 就把返回的 promise 置为 rejected 状态
                reject(err);
            })
        })
    })
}

# race 函数

race 函数接收一个 promise 数组,返回一个 promise,返回的 promise 的状态会与 promise 数组中的第一个修改状态的项同步修改成相同的值

static race(promises) {
    return new MyPromise((resolve, reject) => {
        promises.forEach((promise) => {
            // 任意一个修改状态后就修改返回的 promise 的状态
            promise.then((data) => {
                resolve(data);
            }, (err) => {
                reject(err);
            })
        })
    });
}

# resolve 函数

resolve 函数返回一个已经处于 resolved 状态的 promise,接收要传给回调函数的 data

static resolve(data) {
    if (data instanceof MyPromise) return data;
    return new MyPromise((resolve) => {
        resolve(data);
    })
}

# reject 函数

reject 函数返回一个已经处于 reject 状态的 promise,接收要传给回调函数的 data

static reject(err) {
    if (data instanceof MyPromise) return err;
    return new MyPromise((resolve, reject) => {
        reject(err);
    })
}

好了,到这里所有的代码就暂时实现完了,当然我后期会再加些方法上去,不过那是以后的事了,最后放一波完整的代码

const MyPromise = (function () {
    // 对应的三个状态
    const PENDING = "pending";
    const RESOLVED = "resolved";
    const REJECTED = "rejected";
    // 当前 promise 的数据
    const PromiseValue = Symbol("PromiseValue");
    // 当前 promise 的状态
    const PromiseStatus = Symbol("PromiseStatus");
    // 成功时要执行的回调函数列表
    const onFulfilledList = Symbol("onFulfilledList");
    // 失败时要执行的回调函数列表
    const onRejectedList = Symbol("onRejectedList");
    // 更新状态的函数
    const updateStatus = Symbol("updateStatus");
    // 把任务添加到任务队列里异步执行的函数
    const executeAsync = Symbol("executeAsync");
    // 创建串联的 promise
    const createLinkPromise = Symbol("createLinkPromise");
    // 后续处理的通用函数
    const settleHandle = Symbol("settleHandle");
    // 执行 then 绑定的回调,同时 try catch
    const execute = Symbol("execute");
    // 默认的 OnFulfilled
    const defaultOnFulfilled = function (data) {return data};
    // 默认的 OnRejected
    const defaultOnRejected = function (err) {throw new Error(err)};
    return class  {
        /**
         * 异步执行回调函数
         * @param handler 回调函数
         * @param arg 传递给回调函数的参数
         */
        [executeAsync] (handler, ...arg) {
            setTimeout(function () {
                handler(...arg);
            }, 0)
        }
        /**
         * 更新状态
         * @param newStatus 新的状态
         * @param newValue promise 新的数据
         * @param executeQueue 要执行的回调列表
         */
        [updateStatus] (newStatus, newValue, executeQueue) {
            // 如果状态不是 pending, 就返回
            if (this[PromiseStatus] !== PENDING) return;
            // 把当前 promise 的状态改成新的状态
            this[PromiseStatus] = newStatus;
            // 把当前 promise 的值更新为新的值
            this[PromiseValue] = newValue;
            // 把回调列表里的函数取出来依次执行
            executeQueue.forEach((handler) => {
                // 异步执行 then 中注册的回调
                this[executeAsync] (handler, newValue);
            })
        }
        /**
         * 处理 then 传入的回调函数
         * @param handler 回调函数
         * @param immediatelyStatus 立即推入任务队列执行的状态
         * @param queue 如果不立即执行,保存的队列
         */
        [settleHandle] (handler, immediatelyStatus, queue) {
            // 如果传入的不是一个函数就返回
            if (typeof handler !== "function") return;
            // 判断是不是立刻执行的状态
            if (this[PromiseStatus] === immediatelyStatus) {
                // 是就立刻推入异步执行队列
                this[executeAsync] (handler, this[PromiseValue]);
            } else {
                // 不是就放到回调列表中
                queue.push(handler);
            }
        }
        /**
         * 执行回调,获取返回值,如果报错就执行 reject
         * @param data 传给 handler 的数据
         * @param handler 要在 try catch 中执行的回调函数
         * @param resolve 下个 promise 的 resolve
         * @param reject 下个 promise 的 reject
         */
        [execute] (data, handler, resolve, reject) {
            try {
                const result = handler(data);
                // 如果返回值是 promise
                if (result instanceof MyPromise) {
                    // 用 then 注册回调
                    result.then((data) => {
                        // 把回调收到的数据转发给下个 promise
                        resolve(data);
                    }, (err) => {
                        reject(err);
                    })
                }else {
                    // 如果返回值不是 promise,直接传递就行
                    resolve(result);
                }
            }catch (e) {
                reject(e);
            }
        }
        /**
         * 用于创建 then 返回的新 promise
         * @param onFulfilled then 里传入的 onFulfilled 函数
         * @param onRejected then 里传入的 onRejected 函数
         */
        [createLinkPromise] (onFulfilled, onRejected) {
            return new MyPromise((resolve, reject) => {
                // 在这里调用 [settleHandle] 就能处理 then 传入的回调了
                this[settleHandle]((data) => {
                    this[execute](data, onFulfilled, resolve, reject);
                }, RESOLVED, this[onFulfilledList]);
                this[settleHandle]((err) => {
                    this[execute](err, onRejected, resolve, reject);
                }, REJECTED, this[onRejectedList]);
            });
        }
        then(onFulfilled = defaultOnFulfilled, onRejected = defaultOnRejected) {
            return this[createLinkPromise](onFulfilled, onRejected);
        }
        catch(onRejected) {
            return this.then(defaultOnFulfilled, onRejected);
        }
        finally(handler) {
            return this.then(handler, handler);
        }
        constructor(executor) {
            this[PromiseStatus] = PENDING;
            this[PromiseValue] = undefined;
            this[onFulfilledList] = [];
            this[onRejectedList] = [];
            // 定义 resolve 函数
            const resolve = (data) => {
                this[updateStatus](RESOLVED, data, this[onFulfilledList]);
            };
            // 定义 reject 函数
            const reject = (reason) => {
                this[updateStatus](REJECTED, reason, this[onRejectedList]);
            };
            try {
                // 同步执行 new Promise 时传入的函数
                executor && executor(resolve, reject);
            }catch (e) {
                // 如果捕获到异常就调用 reject
                reject(e);
            }
        }
        static all(promises) {
            //promise 数组的长度
            let length = promises.length;
            // 创建用于保存结果的数组
            let resultArr = new Array(length);
            // 已经处于 resolved 状态的 promise 的个数
            let count = 0;
            return new MyPromise((resolve, reject) => {
                promises.map((promise, index) => {
                    // 给每个 promise 都注册回调
                    promise.then((data) => {
                        // 处于 resolve 状态后,把 count 加 1
                        count ++;
                        // 保存数据到数组中
                        resultArr[index] = data;
                        // 如果全部处于 resolved 状态
                        if (count >= length) {
                            // 就把返回的 promise 变成 resolved 状态
                            resolve(resultArr);
                        }
                    }, (err) => {
                        // 有任一个处于 rejected 状态
                        // 就把返回的 promise 置为 rejected 状态
                        reject(err);
                    })
                })
            })
        }
        static race(promises) {
            return new MyPromise((resolve, reject) => {
                promises.forEach((promise) => {
                    // 任意一个修改状态后就修改返回的 promise 的状态
                    promise.then((data) => {
                        resolve(data);
                    }, (err) => {
                        reject(err);
                    })
                })
            });
        }
        static resolve(data) {
            if (data instanceof MyPromise) return data;
            return new MyPromise((resolve) => {
                resolve(data);
            })
        }
        static reject(err) {
            if (data instanceof MyPromise) return err;
            return new MyPromise((resolve, reject) => {
                reject(err);
            })
        }
    }
})();

感谢你的阅读