日韩无码专区无码一级三级片|91人人爱网站中日韩无码电影|厨房大战丰满熟妇|AV高清无码在线免费观看|另类AV日韩少妇熟女|中文日本大黄一级黄色片|色情在线视频免费|亚洲成人特黄a片|黄片wwwav色图欧美|欧亚乱色一区二区三区

RELATEED CONSULTING
相關(guān)咨詢
選擇下列產(chǎn)品馬上在線溝通
服務(wù)時(shí)間:8:30-17:00
你可能遇到了下面的問題
關(guān)閉右側(cè)工具欄

新聞中心

這里有您想知道的互聯(lián)網(wǎng)營銷解決方案
這一次,徹底弄懂Promise原理

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

為宜城等地區(qū)用戶提供了全套網(wǎng)頁設(shè)計(jì)制作服務(wù),及宜城網(wǎng)站建設(shè)行業(yè)解決方案。主營業(yè)務(wù)為網(wǎng)站制作、網(wǎng)站設(shè)計(jì)、宜城網(wǎng)站設(shè)計(jì),以傳統(tǒng)方式定制建設(shè)網(wǎng)站,并提供域名空間備案等一條龍服務(wù),秉承以專業(yè)、用心的態(tài)度為用戶提供真誠的服務(wù)。我們深信只要達(dá)到每一位用戶的要求,就會(huì)得到認(rèn)可,從而選擇與我們長期合作。這樣,我們也可以走得更遠(yuǎn)!

基本過程:

  1.  初始化 Promise 狀態(tài)(pending)
  2.  執(zhí)行 then(..) 注冊回調(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)后,即開始進(jìn)行下一個(gè)promise.

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

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

 
 
 
 
  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。由此可以看出幾點(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)容沒有輸出,即 Promise 是有狀態(tài)且狀態(tài)只可以由pending -> fulfilled或 pending-> rejected,是不可逆的。
  3.  then 中返回了新的 Promise,但是then中注冊的回調(diào)仍然是屬于上一個(gè) Promise 的。

基于以上幾點(diǎn),我們先寫個(gè)基于 PromiseA+ 規(guī)范的只含 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 方法,放到前一個(gè) promise 的回調(diào)對象中  
  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) //處理回調(diào)  
  24.                callback.resolve(ret) //處理下一個(gè) 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+ 規(guī)范  
  35.        }  
  36.        function handelCb(){  
  37.            while(callbacks.length) {  
  38.                const fulfiledFn = callbacks.shift();  
  39.                handle(fulfiledFn);  
  40.            };  
  41.        }  
  42.        fn(resolve)  
  43.    } 

這個(gè)模型簡單易懂,這里最關(guān)鍵的點(diǎn)就是在 then 中新創(chuàng)建的 Promise,它的狀態(tài)變?yōu)?fulfilled 的節(jié)點(diǎn)是在上一個(gè) Promise的回調(diào)執(zhí)行完畢的時(shí)候。也就是說當(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),以此類推下去…鏈?zhǔn)秸{(diào)用的效應(yīng)就出來了。

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

 
 
 
 
  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 

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

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

用上面的 Promise 模型,得到的結(jié)果顯然不是我們想要的。認(rèn)真看上面的模型,執(zhí)行 callback.resolve 時(shí),傳入的參數(shù)是 callback.onFulfilled 執(zhí)行完成的返回,顯然這個(gè)測試?yán)臃祷氐木褪且粋€(gè) 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 為新產(chǎn)生的 Promise,此時(shí)resolve為上個(gè) promise 的resolve  
  10.                       //相當(dāng)于調(diào)用了新產(chǎn)生 Promise 的then方法,注入了上個(gè) promise 的resolve 為其回調(diào)  
  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.   } 

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

 
 
 
 
  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 入?yún)?Promise 的時(shí)候的處理。我們觀察一下 test 里面創(chuàng)建的 Promise,它是沒有調(diào)用 then方法的。從上面的分析我們已經(jīng)知道 Promise 的回調(diào)函數(shù)就是通過調(diào)用其 then 方法注冊的,因此 test 里面創(chuàng)建的 Promise 其回調(diào)函數(shù)為空。

顯然如果沒有回調(diào)函數(shù),執(zhí)行 resolve 的時(shí)候,是沒辦法鏈?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 來綁定 this 的指向。

基于新的 Promise 模型,上面的執(zhí)行過程產(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

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

異常處理

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

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.     } 

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

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

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

在實(shí)際應(yīng)用的時(shí)候,我們很容易會(huì)碰到這樣的場景,不管Promise最后的狀態(tài)如何,都要執(zhí)行一些最后的操作。我們把這些操作放到 finally 中,也就是說 finally 注冊的函數(shù)是與 Promise 的狀態(tài)無關(guān)的,不依賴 Promise 的執(zhí)行結(jié)果。所以我們可以這樣寫 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 方法

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

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

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

  •  無參數(shù) [直接返回一個(gè)resolved狀態(tài)的 Promise 對象]
  •  普通數(shù)據(jù)對象 [直接返回一個(gè)resolved狀態(tài)的 Promise 對象]
  •  一個(gè)Promise實(shí)例 [直接返回當(dāng)前實(shí)例]
  •  一個(gè)thenable對象(thenable對象指的是具有then方法的對象) [轉(zhuǎn)為 Promise 對象,并立即執(zhí)行thenable對象的then方法。]

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

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

 
 
 
 
  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

入?yún)⑹且粋€(gè) Promise 的實(shí)例數(shù)組,然后注冊一個(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í)行完畢。

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

 
 
 
 
  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.     }   

總結(jié)

Promise 源碼不過幾百行,我們可以從執(zhí)行結(jié)果出發(fā),分析每一步的執(zhí)行過程,然后思考其作用即可。其中最關(guān)鍵的點(diǎn)就是要理解 then 函數(shù)是負(fù)責(zé)注冊回調(diào)的,真正的執(zhí)行是在 Promise 的狀態(tài)被改變之后。而當(dāng) resolve 的入?yún)⑹且粋€(gè) Promise 時(shí),要想鏈?zhǔn)秸{(diào)用起來,就必須調(diào)用其 then 方法(then.call),將上一個(gè) Promise 的 resolve 方法注入其回調(diào)數(shù)組中。

參考資料

  •  PromiseA+規(guī)范
  •  Promise 實(shí)現(xiàn)原理精解
  •  30分鐘,讓你徹底明白Promise原理

完整 Promise 模型

 
 
 
 
  1. function Promise(fn) {  
  2.   let state = 'pending'  
  3.   let value =  網(wǎng)站欄目:這一次,徹底弄懂Promise原理
    文章URL:http://m.5511xx.com/article/dhgigse.html