这一次,彻底弄懂Promise原理

2019-11-09 14:13| 发布者: 汇众注册平台| 查看: |

这一次,彻底弄懂Promise原理

Promise 必要为以下三种情态之一:等待态(Pending)、实行态(Fulfilled)和回绝态(Rejected)。一旦Promise 被 resolve 或 reject,不能再迁徙至其余所有情态(即情态 immutable)。

基本过程:

 初始化 Promise 情态(pending)

 实行 then(..) 注册回调解置数组(then 方法可被同一个 promise 调用多次)

 马上实行 Promise 中传入的 fn 函数,将Promise 内部 resolve、reject 函数作为参数传递给 fn ,按事变机制时机处理

 Promise中要保证,then方法传入的参数 onFulfilled 和 onRejected,必要在then方法被调用的那一轮事变循环以后的新实行栈中实行。

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

链式调用

先从 Promise 实行成果看一下,犹如下一段代码:

new Promise((resolve, reject) => {  

      setTimeout(() => {  

          resolve({ test: 1 })  

          resolve({ test: 2 })  

          reject({ test: 2 })  

      }, 1000)  

  }).then((data) => {  

      console.log('result1', data)  

  },(data1)=> 

      console.log('result2',data1)  

  }).then((data) => {  

      console.log('result3', data)  

  })  

  //result1 { test: 1 }  

  //result3 undefined 

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

 可进行链式调用,且每次 then 返回了新的 Promise(2次打印成果不一样,要是是同一个实例,打印成果应该一样。

 只输出第一次 resolve 的内容,reject 的内容没有输出,即 Promise 是有情态且情态只可以由pending -> fulfilled或 pending-> rejected,是不可逆的。

 then 中返回了新的 Promise,但是then中注册的回调依然是属于上一个 Promise 的。

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

function Promise(fn){   

       let state = 'pending' 

       let value = null 

       const callbacks = [];  

       this.then = function (onFulfilled){  

           return new Promise((resolve, reject)=> 

               handle({ //桥梁,将新 Promise 的 resolve 方法,放到前一个 promise 的回调对象中  

                   onFulfilled,   

                   resolve  

               })  

           })  

       }  

       function handle(callback){  

           if(state === 'pending'){  

               callbacks.push(callback)  

               return;  

           }  

           if(state === 'fulfilled'){  

               if(!callback.onFulfilled){  

                   callback.resolve(value)  

                   return;  

               }  

本报谈论:

               const ret = callback.onFulfilled(value) //处理回调  

               callback.resolve(ret) //处理下一个 promise 的resolve  

           }  

       }  

       function resolve(newValue){  

           const fn = ()=> 

               if(state !== 'pending')return  

               state = 'fulfilled' 

               value = newValue  

               handelCb()  

           }  

           setTimeout(fn,0) //基于 PromiseA+ 规范  

       }  

       function handelCb(){  

           while(callbacks.length) {  

               const fulfiledFn = callbacks.shift();  

               handle(fulfiledFn);  

           };  

       }  

       fn(resolve)  

   } 

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

但是要是仅仅是例子中的状况,咱们可以这么写: 

new Promise((resolve, reject) => {  

        setTimeout(() => {  

            resolve({ test: 1 })  

        }, 1000)  

    }).then((data) => {  

        console.log('result1', data) 

        //dosomething  

        console.log('result3')  

    })  

    //result1 { test: 1 }  

    //result3 

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

new Promise((resolve, reject) => {  

  setTimeout(() => {  

    resolve({ test: 1 })  

  }, 1000)  

}).then((data) => {  

  console.log('result1', data)  

  //dosomething  

  return test()  

}).then((data) => {  

  console.log('result2', data)  

})  

function test(id) {  

  return new Promise(((resolve) => {  

    setTimeout(() => {  

      resolve({ test: 2 })  

    }, 5000)  

  }))  

 

//基于第一个 Promise 模型,实行后的输出  

//result1 { test: 1 }  

//result2 Promise {then: ƒ} 

本报谈论:

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

function Promise(fn){   

      ...  

      function resolve(newValue){  

          const fn = ()=> 

              if(state !== 'pending')return  

              if(newValue && (typeof newValue === 'object' || typeof newValue === 'function')){  

                  const {then} = newValue  

                  if(typeof then === 'function'){  

本报谈论:

                      // newValue 为新产生的 Promise,此时resolve为上个 promise 的resolve  

                      //相等于调用了新产生 Promise 的then方法,注入了上个 promise 的resolve 为其回调  

                      then.call(newValue,resolve)  

                      return  

                  }  

              }  

              state = 'fulfilled' 

              value = newValue  

              handelCb()  

          }  

          setTimeout(fn,0)  

      }  

      ...  

  } 

用这个模型,再测试咱们的例子,就得到了准确的成果: 

new Promise((resolve, reject) => {  

        setTimeout(() => {  

            resolve({ test: 1 })  

        }, 1000)  

    }).then((data) => {  

        console.log('result1', data)  

        //dosomething  

        return test()  

    }).then((data) => {  

        console.log('result2', data)  

    })  

    function test(id) {  

        return new Promise(((resolve, reject) => {  

            setTimeout(() => {  

            resolve({ test: 2 })  

            }, 5000)  

        }))  

    }  

    //result1 { test: 1 }  

    //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 实例及其回调函数,可以用看下表:

Promise callback
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 的逻辑即可。

齐全代码如下: 

function Promise(fn){   

        let state = 'pending' 

        let value = null 

        const callbacks = [];  

        this.then = function (onFulfilled,onRejected){  

            return new Promise((resolve, reject)=> 

                handle({  

                    onFulfilled,   

                    onRejected,  

                    resolve,   

                    reject  

                })  

            })  

        }  

        function handle(callback){  

            if(state === 'pending'){  

                callbacks.push(callback)  

                return;  

            }  

            const cb = state === 'fulfilled' ? callback.onFulfilled:callback.onRejected;  

            const next = state === 'fulfilled'? callback.resolve:callback.reject;  

            if(!cb){  

                next(value)  

                return;  

            }  

本报谈论:

            const ret = cb(value)  

            next(ret)  

        }  

        function resolve(newValue){  

            const fn = ()=> 

                if(state !== 'pending')return  

                if(newValue && (typeof newValue === 'object' || typeof newValue === 'function')){  

                    const {then} = newValue  

                    if(typeof then === 'function'){  

                        // newValue 为新产生的 Promise,此时resolve为上个 promise 的resolve  

                        //相等于调用了新产生 Promise 的then方法,注入了上个 promise 的resolve 为其回调  

                        then.call(newValue,resolve, reject)  

                        return  

                    }  

                }  

                state = 'fulfilled' 

                value = newValue  

                handelCb()  

            }  

            setTimeout(fn,0)  

        }  

        function reject(error){  

            const fn = ()=> 

                if(state !== 'pending')return  

                if(error && (typeof error === 'object' || typeof error === 'function')){  

                    const {then} = error  

本报谈论:

                    if(typeof then === 'function'){  

                        then.call(error,resolve, reject)  

                        return  

                    }  

                }  

                state = 'rejected' 

                value = error  

                handelCb()  

            }  

            setTimeout(fn,0)  

        }  

        function handelCb(){  

            while(callbacks.length) {  

                const fn = callbacks.shift();  

                handle(fn);  

            };  

        }  

        fn(resolve, reject)  

    } 

十分处理

十分通常是指在实行失败/胜利回调时期码出错产生的错误,对于这类十分,咱们利用 try-catch 来捕获错误,并将 Promise 设为 rejected 情态即可。

handle代码改革如下: 

function handle(callback){  

        if(state === 'pending'){  

            callbacks.push(callback)  

            return;  

        }  

        const cb = state === 'fulfilled' ? callback.onFulfilled:callback.onRejected;  

        const next = state === 'fulfilled'? callback.resolve:callback.reject;  

        if(!cb){  

            next(value)  

            return;  

        }  

        try {  

本报谈论:

            const ret = cb(value)  

            next(ret)  

        } catch (e) {  

            callback.reject(e);  

        }    

    } 

咱们实际利用时,常习气注册 catch 方法来处理错误,例:

new Promise((resolve, reject) => {  

     setTimeout(() => {  

         resolve({ test: 1 })  

     }, 1000)  

 }).then((data) => {  

     console.log('result1', data)  

     //dosomething  

     return test()  

 }).catch((ex) => {  

     console.log('error', ex)  

 }) 

实际上,错误也好,十分也罢,最后都是经过reject实现的。也就是说可以经过 then 中的错误回调来处理。因此咱们可以增添这么的一个 catch 方法: 

function Promise(fn){   

       ...  

       this.then = function (onFulfilled,onRejected){  

           return new Promise((resolve, reject)=> 

               handle({  

                   onFulfilled,   

                   onRejected,  

                   resolve,   

                   reject  

               })  

           })  

       }  

       this.catch = function (onError){  

           this.then(null,onError)  

       }  

       ...  

   } 

Finally方法

在实际运用的时间,咱们很轻易会碰到这么的场景,无论Promise最终的情态怎样样,都要实行一点儿最终的操作。咱们把这些操作放到 finally 中,也就是说 finally 注册的函数是与 Promise 的情态无关的,不依附 Promise 的实行成果。因此咱们可以这么写 finally 的逻辑: 

function Promise(fn){   

        ...  

        this.catch = function (onError){  

            this.then(null,onError)  

        }  

        this.finally = function (onDone){  

            this.then(onDone,onError)  

        }  

        ... 

    } 

resolve 方法和 reject 方法

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

Promise.resolve({name:'winty'})  

Promise.reject({name:'winty'})  

// 等价于  

new Promise(resolve => resolve({name:'winty'}))  

new Promise((resolve,reject) => reject({name:'winty'})) 

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

 无参数 [直接返回一个resolved情态的 Promise 对象]

 闻名数据对象 [直接返回一个resolved情态的 Promise 对象]

 一个Promise实例 [直接返回眼前实例]

 一个thenable对象(thenable对象指的是具备then方法的对象) [转为 Promise 对象,并马上实行thenable对象的then方法。]

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

function Promise(fn){   

        ... 

本报谈论:

         this.resolve = function (value){  

            if (value && value instanceof Promise) {  

                return value;  

            } else if (value && typeof value === 'object' && typeof value.then === 'function'){  

                let then = value.then;  

                return new Promise(resolve => {  

                    then(resolve);  

                });  

            } else if (value) {  

                return new Promise(resolve => resolve(value));  

            } else {  

                return new Promise(resolve => resolve());  

            }  

        }  

        ...  

    } 

本报谈论:

Promise.reject与Promise.resolve相似,分头在于Promise.reject一贯返回一个情态的rejected的Promise实例,而Promise.resolve的参数要是是一个Promise实例的话,返回的是参数对应的Promise实例,因此情态不一 定。

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

function Promise(fn){   

        ...  

        this.reject = function (value){  

            return new Promise(function(resolve, reject) {  

                reject(value);  

            });  

        }  

        ...  

    } 

Promise.all

入参是一个 Promise 的实例数组,然后注册一个 then 方法,然后是数组中的 Promise 实例的情态都转为 fulfilled 以后则实行 then 方法。这里首要就是一个计数逻辑,每当一个 Promise 的情态变为 fulfilled 以后就遗失该实例返回的数据,然后将计数减一,当计数器变为 0 时,代表数组中一切 Promise 实例都实行完成。

function Promise(fn){   

      ...  

      this.all = function (arr){  

          var args = Array.prototype.slice.call(arr);  

          return new Promise(function(resolve, reject) {  

              if(args.length === 0) return resolve([]);  

              var remaining = args.length;  

              function res(i, val) {  

                  try {  

                      if(val && (typeof val === 'object' || typeof val === 'function')) {  

                          var then = val.then;  

                          if(typeof then === 'function') {  

                              then.call(val, function(val) {  

                                  res(i, val);  

                              }, reject);  

                              return;  

                          }  

                      }  

                      args[i] = val;  

本报谈论:

                      if(--remaining === 0) {  

                          resolve(args);  

                      }  

                  } catch(ex) {  

                      reject(ex);  

                  }  

              }  

              for(var i = 0; i < args.length; i++) {  

                  res(i, args[i]);  

              }  

          });  

      }  

      ...  

  } 

Promise.race

有了 Promise.all 的糊涂,Promise.race 糊涂起来就更轻易了。它的入参也是一个 Promise 实例数组,然后其 then 注册的回调方法是数组中的某一个 Promise 的情态变为 fulfilled 的时间就实行。由于 Promise 的情态只能改变一次,那么咱们只须要把 Promise.race 中产生的 Promise 对象的 resolve 方法,注入到数组中的每一个 Promise 实例中的回调函数中即可。

function Promise(fn){   

    ...  

    this.race = function(values) {  

        return new Promise(function(resolve, reject) {  

            for(var i = 0len = values.length; i < len; i++) {  

                values[i].then(resolve, reject);  

            }  

        });  

    }  

    ...  

    }   

总结

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

参考资料

 PromiseA+规范

 Promise 实现原理精解

 30分钟,让你彻底迷惑Promise原理

齐全 Promise 模型

function Promise(fn) {  

  let state = 'pending'  

  let value = null  

  const callbacks = []  

  this.then = function (onFulfilled, onRejected) {  

    return new Promise((resolve, reject) => {  

      handle({  

        onFulfilled,  

        onRejected,  

        resolve,  

        reject,  

      })  

    })  

  }  

  this.catch = function (onError) {  

    this.then(null, onError)  

  }  

  this.finally = function (onDone) {  

    this.then(onDone, onError)  

  }  

  this.resolve = function (value) {  

    if (value && value instanceof Promise) {  

      return value  

本报谈论:

    } if (value && typeof value === 'object' && typeof value.then === 'function') {  

      const { then } = value  

      return new Promise((resolve) => {  

        then(resolve)  

      })  

    } if (value) {  

      return new Promise(resolve => resolve(value))  

    }  

    return new Promise(resolve => resolve())  

  }  

  this.reject = function (value) {  

    return new Promise(((resolve, reject) => {  

      reject(value)  

    }))  

  }  

  this.all = function (arr) {  

    const args = Array.prototype.slice.call(arr)  

    return new Promise(((resolve, reject) => {  

      if (args.length === 0) return resolve([])  

      let remaining = args.length  

      function res(i, val) {  

        try {  

本报谈论:

          if (val && (typeof val === 'object' || typeof val === 'function')) {  

            const { then } = val  

            if (typeof then === 'function') {  

              then.call(val, (val) => {  

                res(i, val)  

              }, reject)  

              return  

            }  

          }  

          args[i] = val  

          if (--remaining === 0) {  

            resolve(args)  

          }  

        } catch (ex) {  

          reject(ex)  

        }  

      }  

      for (let i = 0; i < args.length; i++) {  

        res(i, args[i])  

      }  

    }))  

  }  

  this.race = function (values) {  

    return new Promise(((resolve, reject) => {  

      for (let i = 0len = values.length; i < len; i++) {  

        values[i].then(resolve, reject)  

      }  

    }))  

  }  

  function handle(callback) {  

    if (state === 'pending') {  

      callbacks.push(callback)  

      return  

    }  

    const cb = state === 'fulfilled' ? callback.onFulfilled : callback.onRejected  

本报谈论:

    const next = state === 'fulfilled' ? callback.resolve : callback.reject  

    if (!cb) {  

      next(value)  

      return  

    }  

    try {  

      const ret = cb(value)  

      next(ret)  

    } catch (e) {  

      callback.reject(e)  

    }  

  }  

  function resolve(newValue) {  

    const fn = () => {  

      if (state !== 'pending') return  

      if (newValue && (typeof newValue === 'object' || typeof newValue === 'function')) {  

        const { then } = newValue  

        if (typeof then === 'function') {  

          // newValue 为新产生的 Promise,此时resolve为上个 promise 的resolve  

          // 相等于调用了新产生 Promise 的then方法,注入了上个 promise 的resolve 为其回调  

          then.call(newValue, resolve, reject)  

          return  

        }  

      }  

      state = 'fulfilled'  

      value = newValue  

      handelCb()  

    }  

    setTimeout(fn, 0)  

  }  

  function reject(error) {  

    const fn = () => {  

      if (state !== 'pending') return  

      if (error && (typeof error === 'object' || typeof error === 'function')) {  

        const { then } = error  

        if (typeof then === 'function') {  

          then.call(error, resolve, reject)  

          return  

        }  

      }  

      state = 'rejected'  

      value = error  

      handelCb()  

    }  

    setTimeout(fn, 0)  

  }  

  function handelCb() {  

    while (callbacks.length) {  

      const fn = callbacks.shift()  

      handle(fn)  

    }  

  }  

  fn(resolve, reject)  

<
>
汇众平台拥有强大的财团支持,信誉与资金有保障!本站为您提供汇众注册、汇众登录、汇众手机APP客户端下载等。欢迎您的加入,24小时客服在线服务!目前旗下有汇众平台有限公司、汇众平台科技有限公司、汇众平台设备有限公司;致力于建成产品丰富的娱乐业航母!

联系我们

(服务时间:9:00-18:00)

4837899@qq.com

在线咨询 官方微信官方微信

部门热线

前   台:
业务部:
客服部:
技术部:
人事部:

网站建设 微信开发 售后服务 咨询电话 返回顶部
返回顶部