第一篇文章就写给Promise吧

628 阅读4分钟

来掘金已经一年多了,一直处于潜水的状态,除了看看技术大佬的文章,自己却从来没有写过对技术知识的一些总结和感悟,想来真是惭愧。最近又复习了一下Promise,就把第一篇文章写给Promise吧,嗯,promise的意思是承诺(嗯,我是英语专业的┭┮﹏┭┮)。废话不多说,开始进入正题。

先说说回调地狱

我们在进行js异步编程时,经常会用到回调函数,为了实现某些逻辑时,就会出现回调函数的层层嵌套。直接上代码来看一下:

const sayHello = function (name, callback) {
    setTimeout(() => { console.log(name, callback); }, 1000);
}
sayHello('cat', function () {
    sayHello('dog', function () {
        sayHello('rabbit', function () {
            sayHello('sheep', function () {
                sayHello('bird', function () {
                    console.log('套娃结束了')
                })
            })
        })
    })
})

有没有感觉这种代码很恐怖啊,就是我们常说的回调地狱,俗称套娃。

回调地狱使得代码的阅读性变得非常差,而且每个回调函数中,我们都需要对错误进行处理,这样也会导致大量的重复代码出现。

Promise的出现

“铛铛铛铛”~~~ Promise要闪现登场了,就是来解决回调地狱的,它是异步编程的一种解决方案,Promise的本质是一个构造函数,所以我们自然就要通过 new 来构造一个Promise对象了哦~

所以改造后的代码是这样的:

const sayHello = function (name) {
    return new Promise((resolve, reject) => {
        setTimeout(() => { console.log(name); resolve(); }, 1000);
    })
}
sayHello('cat').then(function () {
    return sayHello('dog');
}).then(function () {
    return sayHello('rabbit');
}).then(function () {
    return sayHello('sheep');
}).then(function () {
    return sayHello('bird');
}).then(function () {
    console.log('套娃结束了');
}).catch(function (error) { console.log(error); })

这样写了之后看起比回调地狱那种“套娃”好多了。今天的重点是我要手写一个promise。

先说说几点:

  1. promise接收一个执行器函数(暂且叫executor吧),这个执行器函数的代码是同步执行的。

  2. 执行器函数里面需要定义一个挂起状态:pending。也就是说我们总是要把做的事情从挂起状态推向解决状态,这里的解决状态分为两种,一种是成功状态:resolved,另一种是失败状态:rejected。

  3. executor函数接收两个参数:resolve和reject,这两个参数也是函数,可以理解为回调函数吧,将事情的结果放入resolve函数或reject函数的参数中。

废话不多说,手写一个promise来看下:

(function (window) {
    function Promise(executor) {  //接收executor执行器函数作为参数,executor是同步执行
        const self = this;
        self.status = 'pending';
        self.data = undefined;   //假设数据一开始是undefined的
        self.callbacks = [];   //将回调函数放进数组中,回调函数的数据结构:{ onResolved() {}, onRejected() {} }

        function resolve(value) {  //异步操作成功的函数,参数value就是data数据最终变成的结果数据
            if (self.status !== 'pending') return;
            self.status = 'resolved';
            self.data = value;
            if (self.callbacks.length > 0) {
                setTimeout(() => {
                    self.callbacks.forEach(callbackObj => {
                        callbackObj.onResolved(value);
                    })
                });
            }
        }
        function reject(value) { //异步操作失败的函数
            if (self.status !== 'pending') return;
            self.status = 'rejected';
            self.data = value;
            if (self.callbacks.length > 0) {
                setTimeout(() => {
                    self.callbacks.forEach(callbackObj => {
                        callbackObj.onRejected(value);
                    })
                });
            }
        }

        //立即同步执行executor函数
        try {
            executor(resolve, reject)
        } catch (error) {
            reject(error);  //如果执行器抛出异常,promise对象变为rejected状态
        }
    }

    Promise.prototype.then = function(onResolved, onRejected) {
        onResolved = typeof onResolved === 'function' ? onResolved : value => value;  //向后成功传递的value
        onRejected = typeof onRejected === 'function' ? onRejected : reason => {throw error}; //向后传递失败的error
        const self = this;

        return new Promise((resolve, reject) => {
            //调用指定回调函数处理
            function handle(callback) {
                /**
                 * 根据回调函数的返回值处理:
                 * 1. 返回值是promise,return的promise结果就是这个promise的结果
                 * 2. 返回值不是promise,返回的promise会成功,返回值是value
                 * 3. 直接抛出错误,return的promise会失败,reason就是error
                 */
                try {
                    const result = callback(self.data);
                    if(result instanceof Promise) {  //回调函数返回值是promise
                        result.then(resolve, reject);
                    } else {    //回调函数返回值不是promise
                        resolve(result);
                    }
                } catch (error) {
                    reject(error);
                }
            }
            if(self.status === 'pending') {  //如果当前状态是pending状态,将回调函数保存起来
                self.callbacks.push({
                    onResolved(value) {
                        handle(onResolved);
                    },
                    onRejected(reason) {
                        handle(onRejected);
                    }
                })
            } else if(self.status === 'resolved') {  //假设当前状态是resolved状态
                setTimeout(() => {
                    handle(onResolved);   
                });
            } else {  //假设当前是rejected状态
                setTimeout(() => {
                    handle(onRejected);   
                });
            }
        })
    }

    Promise.prototype.catch = function(onRejected) {
        return this.then(undefined, onRejected);
    }

    Promise.resolve = function(value) {
        //返回一个成功/失败的promise
        return new Promise((resolve, reject) => {
            /**
             * 1. 参数是一个promise
             * 2. 参数直接是一个值
             */
            if(value instanceof Promise) {
                value.then(resolve, reject)
            } else {
                resolve(value);
            }
        })
    }

    Promise.reject = function(reason) {
        //返回一个失败的promise
        return new Promise((resolve, reject) => {
            reject(reason);
        })
    }

    Promise.all = function(promises) {
        let resolvedCount = 0;
        const values = new Array(promises.length);
        return new Promise((resolve, reject) => {
            promises.forEach((p, index) => {
                Promise.resolve(p).then(
                    value => {
                        resolvedCount ++;
                        values[index] = value;
                        if(resolvedCount === promises.length) {
                            resolve(values);
                        }
                    }
                ),
                reason => {
                    reject(reason);
                }
            })
        })
    }

    Promise.race = function(promises) {
        return new Promise((resolve, reject) => {
            promises.forEach((p, index) => {
                Promise.resolve(p).then(
                    value => {
                        resolve(value);
                    }
                )
            }),
            reason => {
                reject(reason);
            }
        })
    }

    window.Promise = Promise;
})(window)