怎么弄懂Promise原理

這篇文章主要介紹“怎么弄懂Promise原理”,在日常操作中,相信很多人在怎么弄懂Promise原理問(wèn)題上存在疑惑,小編查閱了各式資料,整理出簡(jiǎn)單好用的操作方法,希望對(duì)大家解答”怎么弄懂Promise原理”的疑惑有所幫助!接下來(lái),請(qǐng)跟著小編一起來(lái)學(xué)習(xí)吧!

創(chuàng)新互聯(lián)建站主要從事網(wǎng)站建設(shè)、成都網(wǎng)站設(shè)計(jì)、網(wǎng)頁(yè)設(shè)計(jì)、企業(yè)做網(wǎng)站、公司建網(wǎng)站等業(yè)務(wù)。立足成都服務(wù)靜寧,十載網(wǎng)站建設(shè)經(jīng)驗(yàn),價(jià)格優(yōu)惠、服務(wù)專(zhuān)業(yè),歡迎來(lái)電咨詢(xún)建站服務(wù):18982081108

Promise 必須為以下三種狀態(tài)之一:等待態(tài)(Pending)、執(zhí)行態(tài)(Fulfilled)和拒絕態(tài)(Rejected)。一旦Promise 被 resolve 或 reject,不能再遷移至其他任何狀態(tài)(即狀態(tài) immutable)。

基本過(guò)程:

  1.  初始化 Promise 狀態(tài)(pending)

  2.  執(zhí)行 then(..) 注冊(cè)回調(diào)處理數(shù)組(then 方法可被同一個(gè) promise 調(diào)用多次)

  3.  立即執(zhí)行 Promise 中傳入的 fn 函數(shù),將Promise 內(nèi)部 resolve、reject 函數(shù)作為參數(shù)傳遞給 fn ,按事件機(jī)制時(shí)機(jī)處理

  4.  Promise中要保證,then方法傳入的參數(shù) onFulfilled 和 onRejected,必須在then方法被調(diào)用的那一輪事件循環(huán)之后的新執(zhí)行棧中執(zhí)行。

真正的鏈?zhǔn)絇romise是指在當(dāng)前promise達(dá)到fulfilled狀態(tài)后,即開(kāi)始進(jìn)行下一個(gè)promise.

鏈?zhǔn)秸{(diào)用

先從 Promise 執(zhí)行結(jié)果看一下,有如下一段代碼:

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。由此可以看出幾點(diǎn):

  1.  可進(jìn)行鏈?zhǔn)秸{(diào)用,且每次 then 返回了新的 Promise(2次打印結(jié)果不一致,如果是同一個(gè)實(shí)例,打印結(jié)果應(yīng)該一致。

  2.  只輸出第一次 resolve 的內(nèi)容,reject 的內(nèi)容沒(méi)有輸出,即 Promise 是有狀態(tài)且狀態(tài)只可以由pending -> fulfilled或 pending-> rejected,是不可逆的。

  3.  then 中返回了新的 Promise,但是then中注冊(cè)的回調(diào)仍然是屬于上一個(gè) Promise 的。

基于以上幾點(diǎn),我們先寫(xiě)個(gè)基于 PromiseA+ 規(guī)范的只含 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 方法,放到前一個(gè) promise 的回調(diào)對(duì)象中                     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) //處理回調(diào)                 callback.resolve(ret) //處理下一個(gè) promise 的resolve             }         }         function resolve(newValue){             const fn = ()=>{                 if(state !== 'pending')return                 state = 'fulfilled';                 value = newValue                 handelCb()             }             setTimeout(fn,0) //基于 PromiseA+ 規(guī)范         }         function handelCb(){             while(callbacks.length) {                 const fulfiledFn = callbacks.shift();                 handle(fulfiledFn);             };         }         fn(resolve)     }

這個(gè)模型簡(jiǎn)單易懂,這里最關(guān)鍵的點(diǎn)就是在 then 中新創(chuàng)建的 Promise,它的狀態(tài)變?yōu)?fulfilled 的節(jié)點(diǎn)是在上一個(gè) Promise的回調(diào)執(zhí)行完畢的時(shí)候。也就是說(shuō)當(dāng)一個(gè) Promise 的狀態(tài)被 fulfilled 之后,會(huì)執(zhí)行其回調(diào)函數(shù),而回調(diào)函數(shù)返回的結(jié)果會(huì)被當(dāng)作 value,返回給下一個(gè) Promise(也就是then 中產(chǎn)生的 Promise),同時(shí)下一個(gè) Promise的狀態(tài)也會(huì)被改變(執(zhí)行 resolve 或 reject),然后再去執(zhí)行其回調(diào),以此類(lèi)推下去…鏈?zhǔn)秸{(diào)用的效應(yīng)就出來(lái)了。

但是如果僅僅是例子中的情況,我們可以這樣寫(xiě): 

new Promise((resolve, reject) => {          setTimeout(() => {              resolve({ test: 1 })          }, 1000)      }).then((data) => {          console.log('result1', data)         //dosomething          console.log('result3')      })      //result1 { test: 1 }      //result3

實(shí)際上,我們常用的鏈?zhǔn)秸{(diào)用,是用在異步回調(diào)中,以解決"回調(diào)地獄"的問(wèn)題。如下例子:

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)    }))  }  //基于第一個(gè) Promise 模型,執(zhí)行后的輸出  //result1 { test: 1 }  //result2 Promise {then: ƒ}

用上面的 Promise 模型,得到的結(jié)果顯然不是我們想要的。認(rèn)真看上面的模型,執(zhí)行 callback.resolve 時(shí),傳入的參數(shù)是 callback.onFulfilled 執(zhí)行完成的返回,顯然這個(gè)測(cè)試?yán)臃祷氐木褪且粋€(gè) Promise,而我們的 Promise 模型中的 resolve 方法并沒(méi)有特殊處理。那么我們將 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 為新產(chǎn)生的 Promise,此時(shí)resolve為上個(gè) promise 的resolve                        //相當(dāng)于調(diào)用了新產(chǎn)生 Promise 的then方法,注入了上個(gè) promise 的resolve 為其回調(diào)                        then.call(newValue,resolve)                        return                    }                }                state = 'fulfilled';                value = newValue                handelCb()            }            setTimeout(fn,0)        }        ...    }

用這個(gè)模型,再測(cè)試我們的例子,就得到了正確的結(jié)果: 

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 }

顯然,新增的邏輯就是針對(duì) resolve 入?yún)?Promise 的時(shí)候的處理。我們觀察一下 test 里面創(chuàng)建的 Promise,它是沒(méi)有調(diào)用 then方法的。從上面的分析我們已經(jīng)知道 Promise 的回調(diào)函數(shù)就是通過(guò)調(diào)用其 then 方法注冊(cè)的,因此 test 里面創(chuàng)建的 Promise 其回調(diào)函數(shù)為空。

顯然如果沒(méi)有回調(diào)函數(shù),執(zhí)行 resolve 的時(shí)候,是沒(méi)辦法鏈?zhǔn)较氯サ摹R虼?,我們需要主?dòng)為其注入回調(diào)函數(shù)。

我們只要把第一個(gè) then 中產(chǎn)生的 Promise 的 resolve 函數(shù)的執(zhí)行,延遲到 test 里面的 Promise 的狀態(tài)為 onFulfilled 的時(shí)候再執(zhí)行,那么鏈?zhǔn)骄涂梢岳^續(xù)了。所以,當(dāng) resolve 入?yún)?Promise 的時(shí)候,調(diào)用其 then 方法為其注入回調(diào)函數(shù),而注入的是前一個(gè) Promise 的 resolve 方法,所以要用 call 來(lái)綁定 this 的指向。

基于新的 Promise 模型,上面的執(zhí)行過(guò)程產(chǎn)生的 Promise 實(shí)例及其回調(diào)函數(shù),可以用看下表:

Promisecallback
P1[{onFulfilled:c1(第一個(gè)then中的fn),resolve:p2resolve}]
P2 (P1 調(diào)用 then 時(shí)產(chǎn)生)[{onFulfilled:c2(第二個(gè)then中的fn),resolve:p3resolve}]
P3 (P2 調(diào)用 then 時(shí)產(chǎn)生)[]
P4 (執(zhí)行c1中產(chǎn)生[調(diào)用 test ])[{onFulfilled:p2resolve,resolve:p5resolve}]
P5 (調(diào)用p2resolve 時(shí),進(jìn)入 then.call 邏輯中產(chǎn)生)[]

有了這個(gè)表格,我們就可以清晰知道各個(gè)實(shí)例中 callback 執(zhí)行的順序是:

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

以上就是鏈?zhǔn)秸{(diào)用的原理了。

reject

下面我們?cè)賮?lái)補(bǔ)全 reject 的邏輯。只需要在注冊(cè)回調(diào)、狀態(tài)改變時(shí)加上 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 為新產(chǎn)生的 Promise,此時(shí)resolve為上個(gè) promise 的resolve                          //相當(dāng)于調(diào)用了新產(chǎn)生 Promise 的then方法,注入了上個(gè) promise 的resolve 為其回調(diào)                          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)      }

異常處理

異常通常是指在執(zhí)行成功/失敗回調(diào)時(shí)代碼出錯(cuò)產(chǎn)生的錯(cuò)誤,對(duì)于這類(lèi)異常,我們使用 try-catch 來(lái)捕獲錯(cuò)誤,并將 Promise 設(shè)為 rejected 狀態(tài)即可。

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);          }        }

我們實(shí)際使用時(shí),常習(xí)慣注冊(cè) catch 方法來(lái)處理錯(cuò)誤,例:

new Promise((resolve, reject) => {       setTimeout(() => {           resolve({ test: 1 })       }, 1000)   }).then((data) => {       console.log('result1', data)       //dosomething       return test()   }).catch((ex) => {       console.log('error', ex)   })

實(shí)際上,錯(cuò)誤也好,異常也罷,最終都是通過(guò)reject實(shí)現(xiàn)的。也就是說(shuō)可以通過(guò) then 中的錯(cuò)誤回調(diào)來(lái)處理。所以我們可以增加這樣的一個(gè) 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方法

在實(shí)際應(yīng)用的時(shí)候,我們很容易會(huì)碰到這樣的場(chǎng)景,不管Promise最后的狀態(tài)如何,都要執(zhí)行一些最后的操作。我們把這些操作放到 finally 中,也就是說(shuō) finally 注冊(cè)的函數(shù)是與 Promise 的狀態(tài)無(wú)關(guān)的,不依賴(lài) Promise 的執(zhí)行結(jié)果。所以我們可以這樣寫(xiě) finally 的邏輯: 

function Promise(fn){           ...          this.catch = function (onError){              this.then(null,onError)          }          this.finally = function (onDone){              this.then(onDone,onError)          }          ...     }

resolve 方法和 reject 方法

實(shí)際應(yīng)用中,我們可以使用 Promise.resolve 和 Promise.reject 方法,用于將于將非 Promise 實(shí)例包裝為 Promise 實(shí)例。如下例子:

Promise.resolve({name:'winty'})  Promise.reject({name:'winty'})  // 等價(jià)于  new Promise(resolve => resolve({name:'winty'}))  new Promise((resolve,reject) => reject({name:'winty'}))

這些情況下,Promise.resolve 的入?yún)⒖赡苡幸韵聨追N情況:

  •  無(wú)參數(shù) [直接返回一個(gè)resolved狀態(tài)的 Promise 對(duì)象]

  •  普通數(shù)據(jù)對(duì)象 [直接返回一個(gè)resolved狀態(tài)的 Promise 對(duì)象]

  •  一個(gè)Promise實(shí)例 [直接返回當(dāng)前實(shí)例]

  •  一個(gè)thenable對(duì)象(thenable對(duì)象指的是具有then方法的對(duì)象) [轉(zhuǎn)為 Promise 對(duì)象,并立即執(zhí)行thenable對(duì)象的then方法。]

基于以上幾點(diǎn),我們可以實(shí)現(xiàn)一個(gè) 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類(lèi)似,區(qū)別在于Promise.reject始終返回一個(gè)狀態(tài)的rejected的Promise實(shí)例,而Promise.resolve的參數(shù)如果是一個(gè)Promise實(shí)例的話(huà),返回的是參數(shù)對(duì)應(yīng)的Promise實(shí)例,所以狀態(tài)不一 定。

因此,reject 的實(shí)現(xiàn)就簡(jiǎn)單多了,如下: 

function Promise(fn){           ...          this.reject = function (value){              return new Promise(function(resolve, reject) {                  reject(value);              });          }          ...      }

Promise.all

入?yún)⑹且粋€(gè) Promise 的實(shí)例數(shù)組,然后注冊(cè)一個(gè) then 方法,然后是數(shù)組中的 Promise 實(shí)例的狀態(tài)都轉(zhuǎn)為 fulfilled 之后則執(zhí)行 then 方法。這里主要就是一個(gè)計(jì)數(shù)邏輯,每當(dāng)一個(gè) Promise 的狀態(tài)變?yōu)?fulfilled 之后就保存該實(shí)例返回的數(shù)據(jù),然后將計(jì)數(shù)減一,當(dāng)計(jì)數(shù)器變?yōu)?0 時(shí),代表數(shù)組中所有 Promise 實(shí)例都執(zhí)行完畢。

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 理解起來(lái)就更容易了。它的入?yún)⒁彩且粋€(gè) Promise 實(shí)例數(shù)組,然后其 then 注冊(cè)的回調(diào)方法是數(shù)組中的某一個(gè) Promise 的狀態(tài)變?yōu)?fulfilled 的時(shí)候就執(zhí)行。因?yàn)?Promise 的狀態(tài)只能改變一次,那么我們只需要把 Promise.race 中產(chǎn)生的 Promise 對(duì)象的 resolve 方法,注入到數(shù)組中的每一個(gè) Promise 實(shí)例中的回調(diào)函數(shù)中即可。

function Promise(fn){       ...      this.race = function(values) {          return new Promise(function(resolve, reject) {              for(var i = 0, len = values.length; i < len; i++) {                  values[i].then(resolve, reject);              }          });      }      ...      }

到此,關(guān)于“怎么弄懂Promise原理”的學(xué)習(xí)就結(jié)束了,希望能夠解決大家的疑惑。理論與實(shí)踐的搭配能更好的幫助大家學(xué)習(xí),快去試試吧!若想繼續(xù)學(xué)習(xí)更多相關(guān)知識(shí),請(qǐng)繼續(xù)關(guān)注創(chuàng)新互聯(lián)網(wǎng)站,小編會(huì)繼續(xù)努力為大家?guī)?lái)更多實(shí)用的文章!

本文題目:怎么弄懂Promise原理
路徑分享:http://bm7419.com/article34/phdcpe.html

成都網(wǎng)站建設(shè)公司_創(chuàng)新互聯(lián),為您提供面包屑導(dǎo)航、自適應(yīng)網(wǎng)站、定制開(kāi)發(fā)、網(wǎng)站建設(shè)網(wǎng)站策劃、網(wǎng)站營(yíng)銷(xiāo)

廣告

聲明:本網(wǎng)站發(fā)布的內(nèi)容(圖片、視頻和文字)以用戶(hù)投稿、用戶(hù)轉(zhuǎn)載內(nèi)容為主,如果涉及侵權(quán)請(qǐng)盡快告知,我們將會(huì)在第一時(shí)間刪除。文章觀點(diǎn)不代表本網(wǎng)站立場(chǎng),如需處理請(qǐng)聯(lián)系客服。電話(huà):028-86922220;郵箱:631063699@qq.com。內(nèi)容未經(jīng)允許不得轉(zhuǎn)載,或轉(zhuǎn)載時(shí)需注明來(lái)源: 創(chuàng)新互聯(lián)

微信小程序開(kāi)發(fā)