手把手带你实现一个Promise

前言

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

实现过程

基本属性

对一个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);
            })
        }
    }
})();


感谢你的阅读

本文链接: https://sakura-snow.com/archives/180

点赞

发表评论

昵称和uid可以选填一个,填邮箱必填(留言回复后将会发邮件给你)
tips:输入uid可以快速获得你的昵称和头像

Title - Artist
0:00