promise从几个关键问题到自定义

1,380 阅读13分钟

一、概述

promise是ES6中最重要也是最难的技术。中大型公司面试,promise的实现是必问的。本文从promise的几个关键问题出发,分析Promise技术难点,到一步步手写promise的实现。当阅读完本文后会对promise的实现原理有深入的理解。

二、promise的几个关键问题

本节内容重点解答promise的几个关键问题,为自定义promise做铺垫。

1、什么是Promise ?

抽象表述:js中进行异步编程的新的解决方案,旧的方案是纯回调方式(存在回调地狱的问题)。

具体表述:

(1)从语法上来说:promise是一个构造函数。

(2)从功能上来说promise对象用来封装一个异步操作并可以获取其结果。

2、为什么要用promise?

(1)、指定回调函数的方式更加灵活。

promise之前:回调必须在启动异步任务之前指定。
promise: 启动异步任务 => 返回promise对象 => 给promise对象绑定回调函数(甚至可以在异步任务结束后指定多个回调)。

(2)、支持链式调用,可以解决回调地狱的问题。

3、promise的状态如何改变?

promise有3中状态,分别是pending、resolved、rejected。状态改变只能从pending 变为resolved,或者从pending变为rejected。执行resolve(value)函数当前为pending就会变成resolved状态,变为resolved后会去执行then中回调onResolved,并且会返回一个新的promise。执行reject(reason)函数当前为pending就会变为rejected,状态变为rejected后会执行then中回调onRejected,也会返回一个新的promise。抛出异常的时候当前是pending就会变为rejected。一个promise对象的状态只能改变一次,无论变为成功还是失败,都会有一个结果数据。成功的结果数据一般成为value,失败的结果数据一般称为reason。基本流程如下:

4、改变promise的状态和指定回调函数谁先谁后?什么时候才能得到数据?

都有可能,正常情况下是先指定回调再改变状态,但也可以先改变状态再指定回调。

(1)、先指定回调函数,再改变状态

 new Promise((resolve,reject) => {
    setTimeout(() => { // 后改变状态,同时指定了数据,异步执行回调函数
        resolve(1)
    },1000)
    }).then( // 这种情况是先指定回调函数,保存当前指定的回调函数
        value => {},
        reason => {}
    )

因为promise的状态改变是在resolve或者reject函数执行过程中进行的。上述代码把resolve放在定时器中,因此会在下一个事件循环中执行。在此之前then中的回调函数(onResolved、onRejected)已指定,会保存在一个数组中。等resolve执行时候再取出来执行。

(2)、先改变状态,再执行回调函数

new Promise((resolve,reject) => {
    // 先改变状态,同时指定了数据
        resolve(1)
    }).then( // 这种情况是后指定回调函数,异步执行回调函数
        value => {},
        reason => {}
    )

上述代码先执行resove(1)改变promise的状态,再指定并执行回调函数。

如果先指定的回调,那当状态发生改变时,回调函数就会调用,得到数据。如果先改变的状态,那当指定回调时,回调函数就会调用,得到数据。

5 、promise.then()返回的新的promise的结果状态由什么决定?

(1)简单表述:由then()指定的回调函数执行的结果决定。

(2)详细表述:
a.如果抛出异常,新promise变为rejected,reason为抛出的异常。

new Promise((resolve,reject) => {
   resolve(1);
}).then(
   value => {
       console.log('onResolved1', value);
       throw 5;
   },
   reason => {
       console.log('onRejected1',reason);
   }).then(
       value => {
           console.log('onResolved2', value)
       },
       reason => {
           console.log('onRejected2',reason);
       }
   )

上述代码会输出onResolved1 1和onRejected2 5。这是因为第一个then中onResolved抛出异常并且值为5,新的promise的状态是失败的,因此执行then中onRejected回调函数,并且reason为异常值5。

b.如果返回的是非promise的任意值,新promise变为resolved,value为返回的值。

    new Promise((resolve,reject) => {
    resolve(1);
}).then(
    value => {
        console.log('onResolved1', value);
        return 2;
    },
    reason => {
        console.log('onRejected1',reason);
    }).then(
        value => {
            console.log('onResolved2', value)
        },
        reason => {
            console.log('onRejected2',reason);
        }
    )

上述代码会输出onResolved1 1和onResolved2 2。这是因为第一个then中onResolved返回值2,因此新的promise的状态是成功的,因此执行then中onResolved回调函数,并且value为2。

c.如果返回的是另一个新promise,此promise的结果就会成为新promise的结果。

new Promise((resolve,reject) => {
   resolve(1);
}).then(
   value => {
       console.log('onResolved1', value);
       return Promise.resolve(3)
   },
   reason => {
       console.log('onRejected1',reason);
   }).then(
       value => {
           console.log('onResolved2', value)
       },
       reason => {
           console.log('onRejected2',reason);
       }
   )

上述代码输出onResolved1 1和onResolved2 3。因为第一个then中执行onResolved返回的是一个成功的promise,所以接下来会第二个then中的onResolved回调,value为3。

new Promise((resolve,reject) => {
   resolve(1);
}).then(
   value => {
       console.log('onResolved1', value);
       return Promise.reject('fail')
   },
   reason => {
       console.log('onRejected1',reason);
   }).then(
       value => {
           console.log('onResolved2', value)
       },
       reason => {
           console.log('onRejected2',reason);
       }
   )

上述代码输出onResolved1 1和onRejected2 fail。因为第一个then中执行onResolved返回的是一个失败的promise,所以接下来会第二个then中的onRejected回调,reason为‘fail'。

6、promise异常传透?

当使用promise的then链式调用时,可以在最后指定失败的回调。前面任何操作出了异常,若未处理,都会传到最后失败的回调中处理。

new Promise((resolve,reject) => {
    reject(1);
}).then(
    value => {
        console.log('onResolved1', value);
        return Promise.reject('fail');
    },
    ).then(
        value => {
            console.log('onResolved2', value)
        },
    ).catch(reason => {
        console.log('异常传透', reason);
    })

输出:异常传透 1。在then中没有写onRjected回调函数,相当于throw reason。

new Promise((resolve,reject) => {
    reject(1);
}).then(
    value => {
        console.log('onResolved1', value);
        return Promise.reject('fail');
    },
    reason => {
        throw reason;
    }
    ).then(
        value => {
            console.log('onResolved2', value)
        },
        reason => {
        throw reason;
     }
    ).catch(reason => {
        console.log('异常传透', reason);
    })

输出跟上段代码一样的结果。

7、如何中断promise链?

当使用promise的then链式调用时,想在中间中断,不再调用后面的回调函数。那么可以在回调函数中返回一个pending状态的promise对象。

new Promise((resolve,reject) => {
    reject(1);
}).then(
    value => {
        console.log('onResolved1', value);
        return Promise.resolve(3)
    },
    reason => {
        throw reason;
    }).then(
        value => {
            console.log('onResolved2', value)
        },
        reason => {
            throw reason;
        }
    ).catch(reason => {
        console.log('异常传统', reason);
        return new Promise(() => {}) // 返回pending的promise
    }).then(
        value => {
            console.log('value', value);
        },
        reason => {
            console.log('reason', reason);
        }
    )

catch后面的代码就不会执行了。因为catch返回了一个pending状态的promise,在pending状态的时候是不会去执行then中指定的回调函数的。实现了中断promise链的功能。

搞懂了以上几个问题,下面就让我们来自定义promise吧。

三、自定义promise

以下代码就是自己封装的promise。为了方便读者阅读,做了比较多的注释。

(1)函数版本

/* 
自定义Promise函数模块
 */
// es5 自定义模块:IFFE

(function(window) {
    // 定义promise的三个状态
    const PENDING = 'pending';
    const RESOLVED = 'resolved';
    const REJECTED = 'rejected';
    /*
    Promise构造函数
    执行new Promise是需要传入excutor:执行器函数(是同步的)
     */
   function Promise(excutor) {
       let self = this; // 将当前promise对象保存起来
       self.status = PENDING; // 给promise对象指定初始状态
       self.data = undefined; // 给promised对象指定一个用于存储结果数据的属性
       self.callbacks = []; //存储待执行的回调函数, 数据结构定义为:{onResolved(){}, onRejected(){}}
       function resolve(value) {
           // 如果当前状态不是pending,直接结束,因为状态只能改变一次。
           if (self.status !== PENDING) {
               return;
           }
           // 将状态改为resolved
           self.status = RESOLVED;
           // 保存value数据
           self.data = value;
           // 如果有待执行的callback函数,立即异步执行回调
           if (self.callbacks.length) {
               setTimeout(() => { // 放到事件队列中执行所有成功的回调
                   self.callbacks.forEach(callbacksObj => {
                    callbacksObj.onResolved(value);
                })
               })
           }
       }
       function reject(reason) {
           // 如果当前状态不是pending,直接结束,因为状态只能改变一次
           if (self.status !== PENDING) {
               return;
           }
           // 将状态改为rejected
           self.status = REJECTED;
           // 保存reason数据
           self.data = reason;
           // 如果有待执行的callback函数,立即异步执行回调
           if(self.callbacks.length) { // 放到事件队列中执行所有失败的回调
               setTimeout(() => {
                   self.callbacks.forEach(callbacksObj => {
                       callbacksObj.onRejected(reason);
                   })
               })
           }
       }
       // 立即同步执行excutor
       try {
           excutor(resolve,reject);
       } catch(error) { // 如果执行器抛出异常,promise对象变为rejected状态
           reject(error);
       }
   } 
   /*
   Promise原型对象的then()
   指定成功和失败的回调函数
   返回一个新的promise对象
   返回的promise的结果由onResolved/onRejected的执行结果决定
    */
   Promise.prototype.then = function(onResolved, onRejected) {
       onResolved = typeof onResolved === 'function' ? onResolved : value => value // 向后传递成功点value
       // 指定默认的失败的回调,实现异常传透关键点
       onRejected = typeof onRejected === 'function' ? onRejected : reason => {throw reason} // 向后传递失败的reason
       const self = this;
       //返回一个新的promise对象
       return new Promise((resolve,reject) => {
           /*
           调用指定回调函数处理
            */
           function handle(callback) {
               /*
                1、如果抛出异常,return的promise就会失败,reason就是error
                2、如果回调函数返回非promise,那return的promise就会成功,value就是返回的值
                3、如果回调函数返回是promise,return的promise结果就是这个promise的结果
                */
               try {
                    const result = callback(self.data);
                    if (result instanceof Promise) {
                        result.then(
                            value => { // 当result成功时,让return的promise也成功
                                resolve(value);
                            },
                            reason => {// 当result失败时,让return的promise也失败
                                reject(reason);
                            }
                        )
                        // 更高效的写法
                        // result.then(resolve,reject);
                    } else {
                        resolve(result);
                    }
                } catch(error) {
                    reject(error);
                }
           }
           // 当前状态是pending状态,需要保存起来
           // 等待状态变为resolved或rejected,执行相应回调
           // 还要改变新的promise的状态
            if (self.status === PENDING) {
                self.callbacks.push({
                    onResolved(value){
                        handle(onResolved);
                    },
                    onRejected(reason) {
                        handle(onRejected);
                    }
                });
            } else if (self.status === RESOLVED) { // 状态若是为resolved,那么执行onResolved函数
                setTimeout(() => { // 回调函数要异步执行
                   handle(onResolved);
                })
            } else { // 'rejected'
                setTimeout(() => { // 回调函数要异步执行
                   handle(onRejected);
                })
            }
       })
   }
   /*
   Promise原型对象的catch()
   指定失败的回调函数
   返回一个新的promise
    */
   Promise.prototype.catch = function(onRejected) {
        return this.then(undefined,onRejected);
   }
   /*
   Promise函数对象方法resolve
   返回一个指定结果的成功的promise
    */
   Promise.resolve = function(value) {
       // 返回一个成功/失败promise
       return new Promise((resolve, reject) => {
           // value是promise
           if (value instanceof Promise) { // i使用value的结果作为当前promise的结果
               value.then(resolve,reject);
           } else { // value不是promise,promise变为成功,数据是value
             resolve(value);
           }
       })
   }
   /*
   Promise函数对象方法reject
   返回一个指定reason的失败的promise
    */
   Promise.reject = function(reason) {
       // 返回一个失败的promise
       return new Promise((resolve,reject) => {
           reject(reason);
       })
   }
   /*
   Promise函数对象方法all
   返回一个promise,只有当所有promise都成功时才成功,
   否则只要有一个失败的就失败
    */
   Promise.all = function(promises) {
       const values = new Array(promises.length); // 用来保存所有成功value的数组
       let resolvedcount = 0;// 用来保存成功promise的数量
       return new Promise((resolve, reject) => {
           // 遍历promises获取每个promise的结果
           promises.forEach((promise,index) => {
               Promise.resolve(promise).then(
                   value => { // 只能代表当前promise成功了,将成功的value保存到values
                       values[index] = value;
                       resolvedcount ++ ; // 成功的数量加1
                       //如果全部成功了,将return的promise改为成功
                       if (resolvedcount === promises.length) {
                           resolve(values);
                       }
                   },
                   reason => { // 只要有一个失败,return的promise就失败
                        reject(reason);
                   }
               )
           })
       })
   }
   /*
   Promise函数对象方法race
   返回一个promise,其结果由第一个完成的promise决定
    */
   Promise.race = function(promises) {
       return new Promise((resolve, reject) => {
           // 遍历promises获取每个promise的结果,看谁最先完成
           promises.forEach((promise,index) => { // Promise.resolve(promise)兼容传入的数组中传入普通值的情况
                Promise.resolve(promise).then(
                    value => { //一旦有成功了,将return的promise变为成功
                        resolve(value);
                    },
                    reason => { // 一旦有失败了,将return的promise变为失败
                        reject(reason);
                    }
                )
           })
       })
   }
   
   /*
   返回一个promise对象,它在指定的时间后才确定结果
    */
   Promise.resolveDelay = function(value,time) {
       // 返回一个成功/失败promise
       return new Promise((resolve, reject) => {
           setTimeout(() => {
               // value是promise
                if (value instanceof Promise) { // i使用value的结果作为当前promise的结果
                    value.then(resolve,reject);
                } else { // value不是promise,promise变为成功,数据是value
                    resolve(value);
                }
           },time)
       })
   }
   /*
   返回一个promise对象,它在指定的时间后才确定结果
    */
   Promise.rejectDelay = function(reason,time) {
        // 返回一个失败的promise
        return new Promise((resolve,reject) => {
            setTimeout(() => {
                reject(reason);
            },time)
        })
    }
   // 向外暴露Promise函数
   window.Promise = Promise;
})(window)

(2) 类版本

/* 
自定义Promise函数模块
 */

(function(window) {
    const PENDING = 'pending';
    const RESOLVED = 'resolved';
    const REJECTED = 'rejected';
    
    class Promise {
        constructor(excutor) {
            let self = this; // 将当前promise对象保存起来
            self.status = PENDING; // 给promise对象指定初始状态
            self.data = undefined; // 给promised对象指定一个用于存储结果数据的属性
            self.callbacks = []; //存储待执行的回调函数, {onResolved(){}, onRejected(){}}
            function resolve(value) {
                // 如果当前状态不是pending,直接结束,因为状态只能改变一次
                if (self.status !== PENDING) {
                    return;
                }
                // 将状态改为resolved
                self.status = RESOLVED;
                // 保存value数据
                self.data = value;
                // 如果有待执行的callback函数,立即异步执行回调
                if (self.callbacks.length) {
                    setTimeout(() => { // 放到事件队列中执行所有成功的回调
                        self.callbacks.forEach(callbacksObj => {
                            callbacksObj.onResolved(value);
                        })
                    })
                }
            }
            function reject(reason) {
                // 如果当前状态不是pending,直接结束,因为状态只能改变一次
                if (self.status !== PENDING) {
                    return;
                }
                // 将状态改为rejected
                self.status = REJECTED;
                // 保存reason数据
                self.data = reason;
                // 如果有待执行的callback函数,立即异步执行回调
                if(self.callbacks.length) { // 放到事件队列中执行所有失败的回调
                    setTimeout(() => {
                        self.callbacks.forEach(callbacksObj => {
                            callbacksObj.onRejected(reason);
                        })
                    })
                }
            }
            // 立即同步执行excutor
            try {
                excutor(resolve,reject);
            } catch(error) { // 如果执行器抛出异常,promise对象变为rejected状态
                reject(error);
            }
        }
        then (onResolved, onRejected) {
            onResolved = typeof onResolved === 'function' ? onResolved : value => value // 向后传递成功点value
            // 指定默认的失败的回调,实现异常传透关键点
            onRejected = typeof onRejected === 'function' ? onRejected : reason => {throw reason} // 向后传递失败的reason
            const self = this;
            //返回一个新的promise对象
            return new Promise((resolve,reject) => {
                /*
                调用指定回调函数处理
                    */
                function handle(callback) {
                    /*
                        1、如果抛出异常,return的promise就会失败,reason就是error
                        2、如果回调函数返回非promise,那return的promise就会成功,value就是返回的值
                        3、如果回调函数返回是promise,return的promise结果就是这个promise的结果
                        */
                    try {
                            const result = callback(self.data);
                            if (result instanceof Promise) {
                                result.then(
                                    value => { // 当result成功时,让return的promise也成功
                                        resolve(value);
                                    },
                                    reason => {// 当result失败时,让return的promise也失败
                                        reject(reason);
                                    }
                                )
                                // 更高效的写法
                                // result.then(resolve,reject);
                            } else {
                                resolve(result);
                            }
                        } catch(error) {
                            reject(error);
                        }
                }
                // 当前状态是pending状态,需要保存起来
                // 等待状态变为resolved或rejected,执行相应回调
                // 还要改变新的promise的状态
                    if (self.status === PENDING) {
                        self.callbacks.push({
                            onResolved(value){
                                handle(onResolved);
                            },
                            onRejected(reason) {
                                handle(onRejected);
                            }
                        });
                    } else if (self.status === RESOLVED) { // 状态若是为resolved,那么执行onResolved函数
                        setTimeout(() => { // 回调函数要异步执行
                        handle(onResolved);
                        })
                    } else { // 'rejected'
                        setTimeout(() => { // 回调函数要异步执行
                        handle(onRejected);
                        })
                    }
            })
        }
        catch (onRejected) {
            return this.then(undefined,onRejected);
        }
       static resolve (value) {
            // 返回一个成功/失败promise
            return new Promise((resolve, reject) => {
                // value是promise
                if (value instanceof Promise) { // i使用value的结果作为当前promise的结果
                    value.then(resolve,reject);
                } else { // value不是promise,promise变为成功,数据是value
                    resolve(value);
                }
            })
        }
       static reject (reason) {
            // 返回一个失败的promise
            return new Promise((resolve,reject) => {
                reject(reason);
            })
        }
        static all(promises) {
            const values = new Array(promises.length); // 用来保存所有成功value的数组
            let resolvedcount = 0;// 用来保存成功promise的数量
            return new Promise((resolve, reject) => {
                // 遍历promises获取每个promise的结果
                promises.forEach((promise,index) => {
                    Promise.resolve(promise).then(
                        value => { // 只能代表当前promise成功了,将成功的value保存到values
                            values[index] = value;
                            resolvedcount ++ ; // 成功的数量加1
                            //如果全部成功了,将return的promise改为成功
                            if (resolvedcount === promises.length) {
                                resolve(values);
                            }
                        },
                        reason => { // 只要有一个失败,return的promise就失败
                                reject(reason);
                        }
                    )
                })
            })
        }
        static race (promises) {
            return new Promise((resolve, reject) => {
                // 遍历promises获取每个promise的结果,看谁最先完成
                promises.forEach((promise,index) => { // Promise.resolve(promise)兼容传入的数组中传入普通值的情况
                        Promise.resolve(promise).then(
                            value => { //一旦有成功了,将return的promise变为成功
                                resolve(value);
                            },
                            reason => { // 一旦有失败了,将return的promise变为失败
                                reject(reason);
                            }
                        )
                })
            })
        }
        static resolveDelay (value,time) {
            // 返回一个成功/失败promise
            return new Promise((resolve, reject) => {
                setTimeout(() => {
                    // value是promise
                        if (value instanceof Promise) { // i使用value的结果作为当前promise的结果
                            value.then(resolve,reject);
                        } else { // value不是promise,promise变为成功,数据是value
                            resolve(value);
                        }
                },time)
            })
        }
        static rejectDelay (reason,time) {
            // 返回一个失败的promise
            return new Promise((resolve,reject) => {
                setTimeout(() => {
                    reject(reason);
                },time)
            })
        }
    }
   window.Promise = Promise;
})(window)