日本综合一区二区|亚洲中文天堂综合|日韩欧美自拍一区|男女精品天堂一区|欧美自拍第6页亚洲成人精品一区|亚洲黄色天堂一区二区成人|超碰91偷拍第一页|日韩av夜夜嗨中文字幕|久久蜜综合视频官网|精美人妻一区二区三区

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

新聞中心

這里有您想知道的互聯(lián)網(wǎng)營銷解決方案
這次徹底搞懂Promise(手寫源碼多注釋篇)

 前言

創(chuàng)新互聯(lián)公司2013年至今,先為吐魯番等服務(wù)建站,吐魯番等地企業(yè),進行企業(yè)商務(wù)咨詢服務(wù)。為吐魯番企業(yè)網(wǎng)站制作PC+手機+微官網(wǎng)三網(wǎng)同步一站式服務(wù)解決您的所有建站問題。

promise 是 js 里面非常重要的一部分,搞懂了 promise 才能更好的去理解 async,await 和 generator。但是往往很多時候就是不理解 promise 的機制,所以這次通過一步步實現(xiàn)一個 promise 來加深自己的印象,提高自己的思維。

大概的架子

通過我們經(jīng)常寫的 promise 語法,我們可以先寫一個大概的架子出來,promise 接受回調(diào),并且調(diào)用,自身帶有三種狀態(tài),pendding, onFulfilled, onRejected,并且 resolve 這個函數(shù)可以讓 pendding 狀態(tài)變成 onFulfilled 狀態(tài),同理 reject 函數(shù)可以讓 pendding 狀態(tài)變成 onRejected 狀態(tài)。我們先把上面描述部分實現(xiàn)了。

 
 
 
 
  1. const PromiseCopy = function (fn) { 
  2.   this.info = { 
  3.     status: "pending", 
  4.     value: "", 
  5.   }; 
  6.   const self = this; 
  7.   self.onFulfilledArr = []; // then函數(shù)里面的第一個回調(diào)函數(shù)的集合 
  8.   self.onRejectedArr = []; // then函數(shù)里面的第二個回調(diào)函數(shù)的集合 
  9.   const resolve = function (value) { 
  10.     // 加這個判斷是為了表示,只有在pendding狀態(tài)下才會去執(zhí)行 
  11.     // 狀態(tài)已經(jīng)變成onFulfilled之后就不能再去改變了 
  12.     // 符合PromiseA+中的2.1.2.1 
  13.     if (self.info.status === "pending") { 
  14.       self.info.status = "onFulfilled"; 
  15.       self.info.value = value; 
  16.       self.onFulfilledArr.forEach((fn) => fn(value)); 
  17.     } 
  18.   }; 
  19.   // 和上面同理符合PromiseA+,2.1.3.1 
  20.   const reject = function (value) { 
  21.     if (self.info.status === "pending") { 
  22.       self.info.status = "onRejected";  
  23.       self.info.value = value; 
  24.       self.onRejectedArr.forEach((fn) => fn(value)); 
  25.     } 
  26.   }; 
  27.   fn(resolve, reject); 
  28. };

resolve 的附加實現(xiàn)

其實寫到這里我們的 resolve 函數(shù)還是有一些功能沒有實現(xiàn)的, 我們知道 調(diào)用 resolve(x), x 的值有好幾種情況,如下

  •  如果 x 是 Promise 實例本身,則拋出錯誤
  •  如果 x 是一個 Promise 對象,那么 then 函數(shù)的執(zhí)行取決這個 x 的狀態(tài),如果 x 也調(diào)用 resolve(y),其中 y 也是一個 promise 對象.那么 then 函數(shù)的執(zhí)行取決于這個 promise 對象,依次類推,直到最后一個 promise 狀態(tài)更改
  •  如果 x 是一個 thenable 對象,就是一個對象包含 then 這個屬性,或者是一個函數(shù)包含一個 then 的靜態(tài)方法,那么直接執(zhí)行 then 函數(shù)
  •  如果 x 是一個普通值,直接變成 onFulfilled 狀態(tài),執(zhí)行后面的 then 函數(shù)

思考

  •  網(wǎng)上實現(xiàn)的大部分 resolve 都是我上面的代碼,但是根據(jù)規(guī)范,resolve 函數(shù)里面應(yīng)該是要判斷上面幾點的,所以我們上面寫的代碼是有誤的
  •  還有一個問題是,我們需要在 resolve 函數(shù)里面判斷 x 是不是實例的本身,但是正常的 resolve 函數(shù)我們經(jīng)常是傳入一個參數(shù),所以中間肯定是有一個中間函數(shù)的,看下面的代碼
 
 
 
 
  1. const PromiseCopy = function (fn) { 
  2.   this.info = { 
  3.     status: "pending",
  4.      value: "", 
  5.   }; 
  6.   const self = this; 
  7.   self.onFulfilledArr = []; // then函數(shù)里面的第一個回調(diào)函數(shù)的集合 
  8.   self.onRejectedArr = []; // then函數(shù)里面的第二個回調(diào)函數(shù)的集合 
  9.   // _resolve 是我們經(jīng)常調(diào)用的resolve 
  10.   // 但是真正實現(xiàn)的應(yīng)該是里面的resolve 
  11.   const _resolve = function (value) { 
  12.     // 這個函數(shù)得改變一下 
  13.     // PromiseCopy一旦被實例化,那么self就是實例本身了 
  14.     resolve(self, value); 
  15.   }; 
  16.   // 此時我們就可以在resolve進行判斷了 
  17.   const resolve = function (promise, value) { 
  18.     let ifexec = false; 
  19.     // 首先判斷value是不是promise本身 
  20.     if (value === promise) { 
  21.       // 一定要用TypeError寫 不然promises-aplus-tests跑不通 
  22.       // 切記這是第一個坑,promises-aplus-tests只認TypeError這種錯誤形式 
  23.       reject(new TypeError("A promise cannot be onFulfilled with itself.")); 
  24.     } 
  25.     // value是一個thenable對象 
  26.     // 這個要Object.prototype.toString.call(value) === "[object Object]"判斷
  27.     // 不然resolve([])有問題,不知道是不是我實現(xiàn)問題 
  28.     if ( 
  29.       value && 
  30.       (Object.prototype.toString.call(value) === "[object Object]" || 
  31.         typeof value === "function") 
  32.     ) { 
  33.       // var promise1 = Promise.resolve(dump).then(function () { 
  34.       //   return { 
  35.       //     then: (resolve, reject) => { 
  36.       //       setTimeout(() => { 
  37.       //         resolve({ 
  38.       //           then: (resolve, reject) => { 
  39.       //             resolve("aa111a"); 
  40.       //             throw "other"; 
  41.       //           }, 
  42.       //         }); 
  43.       //       }); 
  44.       //     }, 
  45.       //   }; 
  46.       // }); 
  47.       // promise1.then( 
  48.       //   (res) => { 
  49.       //     console.log(res === "aa111a"); 
  50.       //     console.log("aaa"); 
  51.       //   }, 
  52.       //   (res) => { 
  53.       //     console.log(res); 
  54.       //     console.log("error"); 
  55.       //   } 
  56.       // ); 
  57.       // 這里的try--catch一定要加 ,不然會promises-aplus-tests會一直報錯,這是第三個大坑 
  58.       // 因為promises-aplus-test測試里面有這一條的 
  59.       // 看上面注釋例子 
  60.       try { 
  61.         // 拿到then函數(shù) 
  62.         const then = value.then; 
  63.         // 如果then是一個函數(shù)則執(zhí)行這個函數(shù) 
  64.         if (typeof then === "function") { 
  65.           // 為什么要.call(value, x, y) 你們可以自己試一下原生的Promise在這種情況下this指向的就是value,所以要綁定 
  66.           // 因為then我們已經(jīng)拿出來了then = value.then,直接調(diào)用then(),this就指向的window 
  67.           // 為什么后面還需要綁定兩個函數(shù)了 
  68.           // 根據(jù)原生的Promise可知,thenable中的then函數(shù)可以接受兩個函數(shù)resolve,reject 
  69.           // 只有手動調(diào)用了resolve和reject才會執(zhí)行后面的.then操作,具體大家自己操作下 
  70.           then.call( 
  71.             value,
  72.              function (value) { 
  73.               if (ifexec) { 
  74.                 return; 
  75.               } 
  76.               // ifexec這個一定要加,不然也會報200ms錯誤,第四個大坑 
  77.               // 目的是為了不讓多次執(zhí)行,語言無法表達看下面的例子 
  78.               // var promise1 = Promise.resolve(dump).then(function () { 
  79.               //   return { 
  80.               //     then: (resolve, reject) => { 
  81.               //       resolve("aa111a"); 
  82.               //       resolve("aa111a"); 
  83.               //     }, 
  84.               //   }; 
  85.               // }); 
  86.               ifexec = true; 
  87.               resolve(promise, value); 
  88.             }, 
  89.             function (value) { 
  90.               if (ifexec) { 
  91.                 return; 
  92.               } 
  93.               ifexec = true; 
  94.               reject(value); 
  95.             } 
  96.           ); 
  97.           return; 
  98.         } 
  99.       } catch (e) { 
  100.         if (ifexec) { 
  101.           return; 
  102.         } 
  103.         ifexec = true; 
  104.         reject(e); 
  105.       }
  106.     } 
  107.     // 下面這一點非常的重要,是async,await 和一些插件比如saga的核心 
  108.     // 就是如果x是一個promise對象,那么then的執(zhí)行取決于x的狀態(tài) 
  109.     // 還有這一個判斷一定要放在這里,不要和上面的換 不然promises-aplus-tests會報一個超過200ms的錯誤,切記這是第二個坑 
  110.     if (value && value instanceof PromiseCopy && value.then === promise.then) { 
  111.       // 將promise的onFulfilledArr給到value 
  112.       // 但是還沒有那么簡單我們要明白兩點 
  113.       // 如果value這個promise已經(jīng)不是pendding,我們給了他也沒有用,所以需要直接調(diào)用 
  114.       if (value.info.status === "pending") { 
  115.         value.onFulfilledArr = self.onFulfilledArr; 
  116.         value.onRejectedArr = self.onRejectedArr; 
  117.       } 
  118.       // 如果value狀態(tài)是onFulfilled 
  119.       if (value.info.status === "onRejected") { 
  120.         self.info.value = value.info.value; 
  121.         self.onRejectedArr.forEach((fn) => fn(value.info.value));
  122.       } 
  123.       // 如果value狀態(tài)是reject 
  124.       if (value.info.status === "onFulfilled") { 
  125.         self.info.value = value.info.value; 
  126.         self.onFulfilledArr.forEach((fn) => fn(value.info.value)); 
  127.       } 
  128.       return; 
  129.     } 
  130.     // 如果是一個普通的值 
  131.     // 加這個判斷是為了表示,只有在pendding狀態(tài)下才會去執(zhí)行 
  132.     // 狀態(tài)已經(jīng)變成onFulfilled之后就不能再去改變了 
  133.     // 符合PromiseA+中的2.1.2.1 
  134.     if (self.info.status === "pending") { 
  135.       self.info.status = "onFulfilled"; 
  136.       self.info.value = value; 
  137.       self.onFulfilledArr.forEach((fn) => fn(value)); 
  138.     } 
  139.   }; 
  140.   // 和上面同理符合PromiseA+,2.1.3.1 
  141.   // reject沒有resolve那么多規(guī)則,比較簡單 
  142.   const reject = function (value) { 
  143.     if (self.info.status === "pending") { 
  144.       self.info.status = "onRejected"; 
  145.       self.info.value = value; 
  146.       self.onRejectedArr.forEach((fn) => fn(value)); 
  147.     } 
  148.   }; 
  149.   // 此時fn調(diào)用的是_reoslve 
  150.   // 這個try catch主要是實現(xiàn)promiseCopy.prototype.catch 
  151.   try { 
  152.     fn(_resolve, reject); 
  153.   } catch (e) { 
  154.     setTimeout(() => { 
  155.       self.onRejectedArr.forEach((fn) => fn(e)); 
  156.     }); 
  157.   } 
  158. };

then 的實現(xiàn)

我們上面介紹的是 promise 的 resolve 用法,promise 還有一個基本用法就是后面接 then,因為是.then 所以我們想到的是這個 then 方法掛在到原型上的,那么 new PromiseCopy 的時候就可以得到這個 then。then 里面是兩個函數(shù),一個是 onFulfilled 后執(zhí)行的回調(diào),一個是 onRejected 后執(zhí)行的回調(diào)。現(xiàn)在的問題是他是怎么做到 then 里面的函數(shù)是在 resolve 和 reject 后執(zhí)行的?這種推遲執(zhí)行或者說在某種情況下去執(zhí)行我們想到的就是觀察者模式了。下面用代碼把上面的話實現(xiàn)一遍,在代碼里面會寫詳細一點的注釋。

 
 
 
 
  1. PromiseCopy.prototype.then = function (onFulfilled, onRejected) { 
  2.   const self = this; 
  3.   // 這里要判斷下,如果PromiseCopy是resolve了那么就直接執(zhí)行onFulfilled 
  4.   if (self.info.status === "onFulfilled") { 
  5.     setTimeout(() => { 
  6.       onFulfilled(self.info.value); 
  7.     }); 
  8.   } 
  9.   if (self.info.status === "onRejected") { 
  10.     setTimeout(() => { 
  11.       onRejected(self.info.value); 
  12.     });
  13.   } 
  14.   // 根據(jù)PromiseA+中的2.2.1.1和2.2.1.2,onFulfilled和onRejected必須是函數(shù),不然就會被忽略 
  15.   if (typeof onFulfilled === "function") { 
  16.     self.onFulfilledArr.push(() => { 
  17.       setTimeout(() => { 
  18.         onFulfilled(self.info.value); 
  19.       }); 
  20.     }); 
  21.   } 
  22.   if (typeof onRejected === "function") { 
  23.     self.onRejectedArr.push(() => { 
  24.       setTimeout(() => { 
  25.         onRejected(self.info.value); 
  26.       }); 
  27.     }); 
  28.   } 
  29.   // 根據(jù)PromiseA+ 2.2.7規(guī)范 then函數(shù)必須返回一個promise對象 
  30.   return new PromiseCopy((resolve, reject) => {}); 
  31. };

then 的額外實現(xiàn)

上面實現(xiàn)的 then 也是一個簡單的用法,不過根據(jù) PromiseA+的規(guī)范這個 then 函數(shù)還有幾個點沒有實現(xiàn),看代碼解釋

 
 
 
 
  1. promise2 = promise1.then(onFulfilled, onRejected); 
  2. promise2.then(onFulfilled, onRejected);
  •  promise1.then 中的 onFulfilled,onRejected 函數(shù)如果返回一個 x,那么當作[[Resolve]](promise2, x)來處理,就跟上面的 resolve 一樣處理,注意如果函數(shù)什么都沒有返回,就是返回的 undefined
  •  promise1.then 函數(shù)中的兩個回調(diào)函數(shù)只要有一個報錯,那么直接調(diào)用 promise2.then 函數(shù)中的錯誤回調(diào)
  •  如果 promise1.then 的第一個回調(diào)不是函數(shù),并且 promise1 調(diào)用的是 resolve,那么 promise2.then 的第一個回調(diào)參數(shù)是 promise1 中 resolve 函數(shù)的拋出值
  •  同理,如果 promise1.then 第二個回調(diào)不是函數(shù),并且 promise1 調(diào)用的是 reject,那么 promise2.then 中的錯誤回調(diào)就會執(zhí)行

思考

如果像上面這么說的話,這個新拋出來的 promise 何時調(diào)用這個 resolve 或者 reject 是一個關(guān)鍵, 并且這個拋出的 promise 的執(zhí)行還得看 onFulfilled 和 onRejected 返回值,這一點當時寫 promise 的時候想了很久,不知道如何組織,后來實在想不出來,看了下網(wǎng)上很多文章,發(fā)現(xiàn)這些邏輯都是在 PromiseCopy 主體里面實現(xiàn)的。

return new PromiseCopy((resolve, reject) => {});

then 實現(xiàn)加強版

 
 
 
 
  1. PromiseCopy.prototype.then = function (onFulfilled, onRejected) { 
  2.   const self = this; 
  3.   // 這個一定要這么寫目的為了讓值傳遞 
  4.   onFulfilled = typeof onFulfilled === "function" ? onFulfilled : (val) => val; 
  5.   // 這個一定要這么寫,一定要拋出一個錯throw err 
  6.   onRejected = 
  7.     typeof onRejected === "function" 
  8.       ? onRejected 
  9.       : (err) => { 
  10.           throw err; 
  11.         }; 
  12.   const newnewPromise = new PromiseCopy((resolve, reject) => { 
  13.     if (self.info.status === "onFulfilled") { 
  14.       setTimeout(() => { 
  15.         try { 
  16.           // 如果onFulfilled不是一個函數(shù)resolve--self.info.value 
  17.           let value = self.info.value; 
  18.           // 這個注釋不要,留著只是為了記錄當時的思路 
  19.           // 這個加判斷是為了防止then函數(shù)逇回調(diào)不是一個函數(shù),,是一個字符串 
  20.           //   if (typeof onFulfilled === "function") { 
  21.           //     value = onFulfilled(value); 
  22.           //   } 
  23.           value = onFulfilled(value); 
  24.           // 這里要做一個[[Resolve]](promise2, x)處理了 
  25.           // 因為resolve里面直接做了,所以直接調(diào)用,和網(wǎng)上的一些實現(xiàn)有點不一樣 
  26.           // 他們是提取了一個resolvePromise函數(shù)調(diào)用,我是直接調(diào)用了resolve 
  27.           resolve(value); 
  28.         } catch (e) { 
  29.           reject(e); 
  30.         } 
  31.       }); 
  32.     } 
  33.     // 注意這里根據(jù)上面可知onFulfilled,onRejected拋出的值都要經(jīng)過[[Resolve]](promise2, x) 
  34.     // 這和resolve,reject不一樣,promise中resolve才走[[Resolve]](promise2, x),reject不走 
  35.     if (self.info.status === "onRejected") { 
  36.       setTimeout(() => { 
  37.         try { 
  38.           let { value } = self.info; 
  39.           value = onRejected(self.info.value);
  40.           resolve(value); 
  41.         } catch (e) { 
  42.           reject(e); 
  43.         } 
  44.       }); 
  45.     } 
  46.     // 如果是pending狀態(tài)也需要push 
  47.     if (self.info.status === "pending") { 
  48.       self.onFulfilledArr.push((data) => { 
  49.         setTimeout(() => { 
  50.           try { 
  51.             let value = data; 
  52.             value = onFulfilled(value); 
  53.             resolve(value); 
  54.           } catch (e) { 
  55.             reject(e); 
  56.           } 
  57.         }); 
  58.       });
  59.       self.onRejectedArr.push((data) => { 
  60.         setTimeout(() => { 
  61.           try { 
  62.             let value = data; 
  63.             value = onRejected(data); 
  64.             resolve(value); 
  65.           } catch (e) { 
  66.             reject(e);
  67.            } 
  68.         }); 
  69.       }); 
  70.     } 
  71.   }); 
  72.   return newPromise; 
  73. };

小結(jié)

到這里 promise 的主體實現(xiàn)已經(jīng)完成了,下面是測試結(jié)果

Promise 其他靜態(tài)方法

Promise.resolve

 
 
 
 
  1. PromiseCopy.resolve = function (data) { 
  2.   return new PromiseCopy((resolve, reject) => { 
  3.     resolve(data); 
  4.   }); 
  5. };

reject

 
 
 
 
  1. Promise.reject = function (reason) { 
  2.   return new Promise((resolve, reject) => { 
  3.     reject(reason); 
  4.   }); 
  5. };

Promise.all

這個方法有幾個特點如下

  •  該方法接受一個數(shù)組,數(shù)組每一個元素都是一個 promise 對象
  •  只有所有 promise 都是 onFulfilled 的時候才會執(zhí)行 then 回調(diào),并且結(jié)果順序和數(shù)組的一致
  •  如果其中一個 promise 發(fā)生了 reject 那么就會返回這個值
 
 
 
 
  1. PromiseCopy.all = function (data) { 
  2.   let count = 0; // 記錄調(diào)用次數(shù) 
  3.   let total = data.length; 
  4.   let result = [];
  5.   return new PromiseCopy((resolve, reject) => { 
  6.     for (let i = 0; i < total; i++) { 
  7.       data[i].then( 
  8.         (res) => { 
  9.           result.push(res); 
  10.           ++count; 
  11.           if (count === totla) { 
  12.             resolve(result); 
  13.           } 
  14.         }, 
  15.         (res) => { 
  16.           return reject(res); 
  17.         } 
  18.       ); 
  19.     } 
  20.   }); 
  21. };

Promise.race

這個方法也有以下幾個特點

  •  這個方法也是接受數(shù)組,數(shù)組的元素是 promise
  •  他只返回最快的那一個 promise 的值
  •  就算有錯誤也會返回最快那一個 promise 的值
 
 
 
 
  1. PromiseCopy.race = function (data) { 
  2.   const total = data.length; 
  3.   return new PromiseCopy((resolve, reject) => { 
  4.     for (let i = 0; i < total; i++) { 
  5.       data[i].then( 
  6.         (res) => { 
  7.           resolve(res); 
  8.         }, 
  9.         (res) => { 
  10.           return reject(res); 
  11.         } 
  12.       ); 
  13.     } 
  14.   }); 
  15. };

catch 方法

 
 
 
 
  1. PromiseCopy.prototype.catch = function (onRejected) { 
  2.   // 能到catch里面來的一定是走的reject的 
  3.   // 而且狀態(tài)一定是pendding 
  4.   const self = this; 
  5.   const newnewPromise = new PromiseCopy((resolve, reject) => { 
  6.     if (self.info.status === "onRejected") { 
  7.       try { 
  8.         setTimeout(() => { 
  9.           let { value } = self.info; 
  10.           if (typeof onRejected === "function") { 
  11.             value = onRejected(self.info.value); 
  12.           } 
  13.           resolve(value); 
  14.         }); 
  15.       } catch (e) { 
  16.         rejetc(e); 
  17.       }
  18.     } 
  19.     if (self.info.status === "pending") { 
  20.       self.onRejectedArr.push((data) => { 
  21.         setTimeout(() => { 
  22.           try { 
  23.             let value = data; 
  24.             if (typeof onRejected === "function") { 
  25.               value = onRejected(data); 
  26.             } 
  27.             resolve(value); 
  28.           } catch (e) { 
  29.             reject(e); 
  30.           } 
  31.         }); 
  32.       }); 
  33.     } 
  34.   }); 
  35.   return newPromise; 
  36. }; 
  37. // 后來發(fā)現(xiàn)catch有一個簡單的實現(xiàn)方法 
  38. // 沒有刪除上面就是為了記錄思路過程 
  39. Promise.prototype.catch = function (onRejected) { 
  40.   return this.then(null, onRejected); 
  41. };

deferred

這個是 Promise 提供的一個快捷使用,自己實現(xiàn) promise 的時候一定要加,不然 promises-aplus-tests promise.js 跑不過

 
 
 
 
  1. PromiseCopyPromiseCopy.defer = PromiseCopy.deferred = function () { 
  2.   let dfd = {}; 
  3.   dfd.promise = new PromiseCopy((resolve, reject) => { 
  4.     dfd.resolve = resolve; 
  5.     dfd.reject = reject; 
  6.   }); 
  7.   return dfd; 
  8. };

新聞名稱:這次徹底搞懂Promise(手寫源碼多注釋篇)
標題鏈接:http://www.dlmjj.cn/article/djdssoe.html