这一次,彻底弄懂Promise原理

Promise 必须为以下三种状态之一:等待态(Pending)、执行态(Fulfilled)和拒绝态(Rejected)。一旦Promise 被 resolve 或 reject,不能再迁移至其他任何状态(即状态 immutable)。

为宜城等地区用户提供了全套网页设计制作服务,及宜城网站建设行业解决方案。主营业务为网站制作、网站设计、宜城网站设计,以传统方式定制建设网站,并提供域名空间备案等一条龙服务,秉承以专业、用心的态度为用户提供真诚的服务。我们深信只要达到每一位用户的要求,就会得到认可,从而选择与我们长期合作。这样,我们也可以走得更远!

基本过程:

  1.  初始化 Promise 状态(pending)
  2.  执行 then(..) 注册回调处理数组(then 方法可被同一个 promise 调用多次)
  3.  立即执行 Promise 中传入的 fn 函数,将Promise 内部 resolve、reject 函数作为参数传递给 fn ,按事件机制时机处理
  4.  Promise中要保证,then方法传入的参数 onFulfilled 和 onRejected,必须在then方法被调用的那一轮事件循环之后的新执行栈中执行。

真正的链式Promise是指在当前promise达到fulfilled状态后,即开始进行下一个promise.

链式调用

先从 Promise 执行结果看一下,有如下一段代码:

 
 
 
 
  1. new Promise((resolve, reject) => {  
  2.       setTimeout(() => {  
  3.           resolve({ test: 1 })  
  4.           resolve({ test: 2 })  
  5.           reject({ test: 2 })  
  6.       }, 1000)  
  7.   }).then((data) => {  
  8.       console.log('result1', data)  
  9.   },(data1)=>{  
  10.       console.log('result2',data1)  
  11.   }).then((data) => {  
  12.       console.log('result3', data)  
  13.   })  
  14.   //result1 { test: 1 }  
  15.   //result3 undefined 

显然这里输出了不同的 data。由此可以看出几点:

  1.  可进行链式调用,且每次 then 返回了新的 Promise(2次打印结果不一致,如果是同一个实例,打印结果应该一致。
  2.  只输出第一次 resolve 的内容,reject 的内容没有输出,即 Promise 是有状态且状态只可以由pending -> fulfilled或 pending-> rejected,是不可逆的。
  3.  then 中返回了新的 Promise,但是then中注册的回调仍然是属于上一个 Promise 的。

基于以上几点,我们先写个基于 PromiseA+ 规范的只含 resolve 方法的 Promise 模型: 

 
 
 
 
  1. function Promise(fn){   
  2.        let state = 'pending';  
  3.        let value = null;  
  4.        const callbacks = [];  
  5.        this.then = function (onFulfilled){  
  6.            return new Promise((resolve, reject)=>{  
  7.                handle({ //桥梁,将新 Promise 的 resolve 方法,放到前一个 promise 的回调对象中  
  8.                    onFulfilled,   
  9.                    resolve  
  10.                })  
  11.            })  
  12.        }  
  13.        function handle(callback){  
  14.            if(state === 'pending'){  
  15.                callbacks.push(callback)  
  16.                return;  
  17.            }  
  18.            if(state === 'fulfilled'){  
  19.                if(!callback.onFulfilled){  
  20.                    callback.resolve(value)  
  21.                    return;  
  22.                }  
  23.                const ret = callback.onFulfilled(value) //处理回调  
  24.                callback.resolve(ret) //处理下一个 promise 的resolve  
  25.            }  
  26.        }  
  27.        function resolve(newValue){  
  28.            const fn = ()=>{  
  29.                if(state !== 'pending')return  
  30.                state = 'fulfilled';  
  31.                value = newValue  
  32.                handelCb()  
  33.            }  
  34.            setTimeout(fn,0) //基于 PromiseA+ 规范  
  35.        }  
  36.        function handelCb(){  
  37.            while(callbacks.length) {  
  38.                const fulfiledFn = callbacks.shift();  
  39.                handle(fulfiledFn);  
  40.            };  
  41.        }  
  42.        fn(resolve)  
  43.    } 

这个模型简单易懂,这里最关键的点就是在 then 中新创建的 Promise,它的状态变为 fulfilled 的节点是在上一个 Promise的回调执行完毕的时候。也就是说当一个 Promise 的状态被 fulfilled 之后,会执行其回调函数,而回调函数返回的结果会被当作 value,返回给下一个 Promise(也就是then 中产生的 Promise),同时下一个 Promise的状态也会被改变(执行 resolve 或 reject),然后再去执行其回调,以此类推下去…链式调用的效应就出来了。

但是如果仅仅是例子中的情况,我们可以这样写: 

 
 
 
 
  1. new Promise((resolve, reject) => {  
  2.         setTimeout(() => {  
  3.             resolve({ test: 1 })  
  4.         }, 1000)  
  5.     }).then((data) => {  
  6.         console.log('result1', data) 
  7.         //dosomething  
  8.         console.log('result3')  
  9.     })  
  10.     //result1 { test: 1 }  
  11.     //result3 

实际上,我们常用的链式调用,是用在异步回调中,以解决"回调地狱"的问题。如下例子:

 
 
 
 
  1. new Promise((resolve, reject) => {  
  2.   setTimeout(() => {  
  3.     resolve({ test: 1 })  
  4.   }, 1000)  
  5. }).then((data) => {  
  6.   console.log('result1', data)  
  7.   //dosomething  
  8.   return test()  
  9. }).then((data) => {  
  10.   console.log('result2', data)  
  11. })  
  12. function test(id) {  
  13.   return new Promise(((resolve) => {  
  14.     setTimeout(() => {  
  15.       resolve({ test: 2 })  
  16.     }, 5000)  
  17.   }))  
  18. }  
  19. //基于第一个 Promise 模型,执行后的输出  
  20. //result1 { test: 1 }  
  21. //result2 Promise {then: ƒ} 

用上面的 Promise 模型,得到的结果显然不是我们想要的。认真看上面的模型,执行 callback.resolve 时,传入的参数是 callback.onFulfilled 执行完成的返回,显然这个测试例子返回的就是一个 Promise,而我们的 Promise 模型中的 resolve 方法并没有特殊处理。那么我们将 resolve 改一下:

 
 
 
 
  1. function Promise(fn){   
  2.       ...  
  3.       function resolve(newValue){  
  4.           const fn = ()=>{  
  5.               if(state !== 'pending')return  
  6.               if(newValue && (typeof newValue === 'object' || typeof newValue === 'function')){  
  7.                   const {then} = newValue  
  8.                   if(typeof then === 'function'){  
  9.                       // newValue 为新产生的 Promise,此时resolve为上个 promise 的resolve  
  10.                       //相当于调用了新产生 Promise 的then方法,注入了上个 promise 的resolve 为其回调  
  11.                       then.call(newValue,resolve)  
  12.                       return  
  13.                   }  
  14.               }  
  15.               state = 'fulfilled';  
  16.               value = newValue  
  17.               handelCb()  
  18.           }  
  19.           setTimeout(fn,0)  
  20.       }  
  21.       ...  
  22.   } 

用这个模型,再测试我们的例子,就得到了正确的结果: 

 
 
 
 
  1. new Promise((resolve, reject) => {  
  2.         setTimeout(() => {  
  3.             resolve({ test: 1 })  
  4.         }, 1000)  
  5.     }).then((data) => {  
  6.         console.log('result1', data)  
  7.         //dosomething  
  8.         return test()  
  9.     }).then((data) => {  
  10.         console.log('result2', data)  
  11.     })  
  12.     function test(id) {  
  13.         return new Promise(((resolve, reject) => {  
  14.             setTimeout(() => {  
  15.             resolve({ test: 2 })  
  16.             }, 5000)  
  17.         }))  
  18.     }  
  19.     //result1 { test: 1 }  
  20.     //result2 { test: 2 } 

显然,新增的逻辑就是针对 resolve 入参为 Promise 的时候的处理。我们观察一下 test 里面创建的 Promise,它是没有调用 then方法的。从上面的分析我们已经知道 Promise 的回调函数就是通过调用其 then 方法注册的,因此 test 里面创建的 Promise 其回调函数为空。

显然如果没有回调函数,执行 resolve 的时候,是没办法链式下去的。因此,我们需要主动为其注入回调函数。

我们只要把第一个 then 中产生的 Promise 的 resolve 函数的执行,延迟到 test 里面的 Promise 的状态为 onFulfilled 的时候再执行,那么链式就可以继续了。所以,当 resolve 入参为 Promise 的时候,调用其 then 方法为其注入回调函数,而注入的是前一个 Promise 的 resolve 方法,所以要用 call 来绑定 this 的指向。

基于新的 Promise 模型,上面的执行过程产生的 Promise 实例及其回调函数,可以用看下表:

Promisecallback
P1[{onFulfilled:c1(第一个then中的fn),resolve:p2resolve}]
P2 (P1 调用 then 时产生)[{onFulfilled:c2(第二个then中的fn),resolve:p3resolve}]
P3 (P2 调用 then 时产生)[]
P4 (执行c1中产生[调用 test ])[{onFulfilled:p2resolve,resolve:p5resolve}]
P5 (调用p2resolve 时,进入 then.call 逻辑中产生)[]

有了这个表格,我们就可以清晰知道各个实例中 callback 执行的顺序是:

c1 -> p2resolve -> c2 -> p3resolve -> [] -> p5resolve -> []

以上就是链式调用的原理了。

reject

下面我们再来补全 reject 的逻辑。只需要在注册回调、状态改变时加上 reject 的逻辑即可。

完整代码如下: 

 
 
 
 
  1. function Promise(fn){   
  2.         let state = 'pending';  
  3.         let value = null;  
  4.         const callbacks = [];  
  5.         this.then = function (onFulfilled,onRejected){  
  6.             return new Promise((resolve, reject)=>{  
  7.                 handle({  
  8.                     onFulfilled,   
  9.                     onRejected,  
  10.                     resolve,   
  11.                     reject  
  12.                 })  
  13.             })  
  14.         }  
  15.         function handle(callback){  
  16.             if(state === 'pending'){  
  17.                 callbacks.push(callback)  
  18.                 return;  
  19.             }  
  20.             const cb = state === 'fulfilled' ? callback.onFulfilled:callback.onRejected;  
  21.             const next = state === 'fulfilled'? callback.resolve:callback.reject;  
  22.             if(!cb){  
  23.                 next(value)  
  24.                 return;  
  25.             }  
  26.             const ret = cb(value)  
  27.             next(ret)  
  28.         }  
  29.         function resolve(newValue){  
  30.             const fn = ()=>{  
  31.                 if(state !== 'pending')return  
  32.                 if(newValue && (typeof newValue === 'object' || typeof newValue === 'function')){  
  33.                     const {then} = newValue  
  34.                     if(typeof then === 'function'){  
  35.                         // newValue 为新产生的 Promise,此时resolve为上个 promise 的resolve  
  36.                         //相当于调用了新产生 Promise 的then方法,注入了上个 promise 的resolve 为其回调  
  37.                         then.call(newValue,resolve, reject)  
  38.                         return  
  39.                     }  
  40.                 }  
  41.                 state = 'fulfilled';  
  42.                 value = newValue  
  43.                 handelCb()  
  44.             }  
  45.             setTimeout(fn,0)  
  46.         }  
  47.         function reject(error){  
  48.             const fn = ()=>{  
  49.                 if(state !== 'pending')return  
  50.                 if(error && (typeof error === 'object' || typeof error === 'function')){  
  51.                     const {then} = error  
  52.                     if(typeof then === 'function'){  
  53.                         then.call(error,resolve, reject)  
  54.                         return  
  55.                     }  
  56.                 }  
  57.                 state = 'rejected';  
  58.                 value = error  
  59.                 handelCb()  
  60.             }  
  61.             setTimeout(fn,0)  
  62.         }  
  63.         function handelCb(){  
  64.             while(callbacks.length) {  
  65.                 const fn = callbacks.shift();  
  66.                 handle(fn);  
  67.             };  
  68.         }  
  69.         fn(resolve, reject)  
  70.     } 

异常处理

异常通常是指在执行成功/失败回调时代码出错产生的错误,对于这类异常,我们使用 try-catch 来捕获错误,并将 Promise 设为 rejected 状态即可。

handle代码改造如下: 

 
 
 
 
  1. function handle(callback){  
  2.         if(state === 'pending'){  
  3.             callbacks.push(callback)  
  4.             return;  
  5.         }  
  6.         const cb = state === 'fulfilled' ? callback.onFulfilled:callback.onRejected;  
  7.         const next = state === 'fulfilled'? callback.resolve:callback.reject;  
  8.         if(!cb){  
  9.             next(value)  
  10.             return;  
  11.         }  
  12.         try {  
  13.             const ret = cb(value)  
  14.             next(ret)  
  15.         } catch (e) {  
  16.             callback.reject(e);  
  17.         }    
  18.     } 

我们实际使用时,常习惯注册 catch 方法来处理错误,例:

 
 
 
 
  1. new Promise((resolve, reject) => {  
  2.      setTimeout(() => {  
  3.          resolve({ test: 1 })  
  4.      }, 1000)  
  5.  }).then((data) => {  
  6.      console.log('result1', data)  
  7.      //dosomething  
  8.      return test()  
  9.  }).catch((ex) => {  
  10.      console.log('error', ex)  
  11.  }) 

实际上,错误也好,异常也罢,最终都是通过reject实现的。也就是说可以通过 then 中的错误回调来处理。所以我们可以增加这样的一个 catch 方法: 

 
 
 
 
  1. function Promise(fn){   
  2.        ...  
  3.        this.then = function (onFulfilled,onRejected){  
  4.            return new Promise((resolve, reject)=>{  
  5.                handle({  
  6.                    onFulfilled,   
  7.                    onRejected,  
  8.                    resolve,   
  9.                    reject  
  10.                })  
  11.            })  
  12.        }  
  13.        this.catch = function (onError){  
  14.            this.then(null,onError)  
  15.        }  
  16.        ...  
  17.    } 

Finally方法

在实际应用的时候,我们很容易会碰到这样的场景,不管Promise最后的状态如何,都要执行一些最后的操作。我们把这些操作放到 finally 中,也就是说 finally 注册的函数是与 Promise 的状态无关的,不依赖 Promise 的执行结果。所以我们可以这样写 finally 的逻辑: 

 
 
 
 
  1. function Promise(fn){   
  2.         ...  
  3.         this.catch = function (onError){  
  4.             this.then(null,onError)  
  5.         }  
  6.         this.finally = function (onDone){  
  7.             this.then(onDone,onError)  
  8.         }  
  9.         ... 
  10.     } 

resolve 方法和 reject 方法

实际应用中,我们可以使用 Promise.resolve 和 Promise.reject 方法,用于将于将非 Promise 实例包装为 Promise 实例。如下例子:

 
 
 
 
  1. Promise.resolve({name:'winty'})  
  2. Promise.reject({name:'winty'})  
  3. // 等价于  
  4. new Promise(resolve => resolve({name:'winty'}))  
  5. new Promise((resolve,reject) => reject({name:'winty'})) 

这些情况下,Promise.resolve 的入参可能有以下几种情况:

  •  无参数 [直接返回一个resolved状态的 Promise 对象]
  •  普通数据对象 [直接返回一个resolved状态的 Promise 对象]
  •  一个Promise实例 [直接返回当前实例]
  •  一个thenable对象(thenable对象指的是具有then方法的对象) [转为 Promise 对象,并立即执行thenable对象的then方法。]

基于以上几点,我们可以实现一个 Promise.resolve 方法如下: 

 
 
 
 
  1. function Promise(fn){   
  2.         ... 
  3.          this.resolve = function (value){  
  4.             if (value && value instanceof Promise) {  
  5.                 return value;  
  6.             } else if (value && typeof value === 'object' && typeof value.then === 'function'){  
  7.                 let then = value.then;  
  8.                 return new Promise(resolve => {  
  9.                     then(resolve);  
  10.                 });  
  11.             } else if (value) {  
  12.                 return new Promise(resolve => resolve(value));  
  13.             } else {  
  14.                 return new Promise(resolve => resolve());  
  15.             }  
  16.         }  
  17.         ...  
  18.     } 

Promise.reject与Promise.resolve类似,区别在于Promise.reject始终返回一个状态的rejected的Promise实例,而Promise.resolve的参数如果是一个Promise实例的话,返回的是参数对应的Promise实例,所以状态不一 定。

因此,reject 的实现就简单多了,如下: 

 
 
 
 
  1. function Promise(fn){   
  2.         ...  
  3.         this.reject = function (value){  
  4.             return new Promise(function(resolve, reject) {  
  5.                 reject(value);  
  6.             });  
  7.         }  
  8.         ...  
  9.     } 

Promise.all

入参是一个 Promise 的实例数组,然后注册一个 then 方法,然后是数组中的 Promise 实例的状态都转为 fulfilled 之后则执行 then 方法。这里主要就是一个计数逻辑,每当一个 Promise 的状态变为 fulfilled 之后就保存该实例返回的数据,然后将计数减一,当计数器变为 0 时,代表数组中所有 Promise 实例都执行完毕。

 
 
 
 
  1. function Promise(fn){   
  2.       ...  
  3.       this.all = function (arr){  
  4.           var args = Array.prototype.slice.call(arr);  
  5.           return new Promise(function(resolve, reject) {  
  6.               if(args.length === 0) return resolve([]);  
  7.               var remaining = args.length;  
  8.               function res(i, val) {  
  9.                   try {  
  10.                       if(val && (typeof val === 'object' || typeof val === 'function')) {  
  11.                           var then = val.then;  
  12.                           if(typeof then === 'function') {  
  13.                               then.call(val, function(val) {  
  14.                                   res(i, val);  
  15.                               }, reject);  
  16.                               return;  
  17.                           }  
  18.                       }  
  19.                       args[i] = val;  
  20.                       if(--remaining === 0) {  
  21.                           resolve(args);  
  22.                       }  
  23.                   } catch(ex) {  
  24.                       reject(ex);  
  25.                   }  
  26.               }  
  27.               for(var i = 0; i < args.length; i++) {  
  28.                   res(i, args[i]);  
  29.               }  
  30.           });  
  31.       }  
  32.       ...  
  33.   } 

Promise.race

有了 Promise.all 的理解,Promise.race 理解起来就更容易了。它的入参也是一个 Promise 实例数组,然后其 then 注册的回调方法是数组中的某一个 Promise 的状态变为 fulfilled 的时候就执行。因为 Promise 的状态只能改变一次,那么我们只需要把 Promise.race 中产生的 Promise 对象的 resolve 方法,注入到数组中的每一个 Promise 实例中的回调函数中即可。

 
 
 
 
  1. function Promise(fn){   
  2.     ...  
  3.     this.race = function(values) {  
  4.         return new Promise(function(resolve, reject) {  
  5.             for(var i = 0, len = values.length; i < len; i++) {  
  6.                 values[i].then(resolve, reject);  
  7.             }  
  8.         });  
  9.     }  
  10.     ...  
  11.     }   

总结

Promise 源码不过几百行,我们可以从执行结果出发,分析每一步的执行过程,然后思考其作用即可。其中最关键的点就是要理解 then 函数是负责注册回调的,真正的执行是在 Promise 的状态被改变之后。而当 resolve 的入参是一个 Promise 时,要想链式调用起来,就必须调用其 then 方法(then.call),将上一个 Promise 的 resolve 方法注入其回调数组中。

参考资料

  •  PromiseA+规范
  •  Promise 实现原理精解
  •  30分钟,让你彻底明白Promise原理

完整 Promise 模型

 
 
 
 
  1. function Promise(fn) {  
  2.   let state = 'pending'  
  3.   let value =  新闻名称:这一次,彻底弄懂Promise原理
    浏览路径:http://www.hantingmc.com/qtweb/news34/232534.html

    网站建设、网络推广公司-创新互联,是专注品牌与效果的网站制作,网络营销seo公司;服务项目有等

    广告

    声明:本网站发布的内容(图片、视频和文字)以用户投稿、用户转载内容为主,如果涉及侵权请尽快告知,我们将会在第一时间删除。文章观点不代表本网站立场,如需处理请联系客服。电话:028-86922220;邮箱:631063699@qq.com。内容未经允许不得转载,或转载时需注明来源: 创新互联