成人免费xxxxx在线视频软件_久久精品久久久_亚洲国产精品久久久_天天色天天色_亚洲人成一区_欧美一级欧美三级在线观看

這次徹底搞懂Promise(手寫源碼多注釋篇)

開發 前端
promise 是 js 里面非常重要的一部分,搞懂了 promise 才能更好的去理解 async,await 和 generator。

[[347964]]

 前言

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

大概的架子

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

  1. const PromiseCopy = function (fn) {  
  2.   this.info = {  
  3.     status: "pending",  
  4.     value: "",  
  5.   };  
  6.   const self = this 
  7.   self.onFulfilledArr = []; // then函數里面的第一個回調函數的集合  
  8.   self.onRejectedArr = []; // then函數里面的第二個回調函數的集合  
  9.   const resolve = function (value) {  
  10.     // 加這個判斷是為了表示,只有在pendding狀態下才會去執行  
  11.     // 狀態已經變成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 的附加實現

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

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

思考

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

then 的實現

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

  1. PromiseCopy.prototype.then = function (onFulfilled, onRejected) {  
  2.   const self = this 
  3.   // 這里要判斷下,如果PromiseCopy是resolve了那么就直接執行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.   // 根據PromiseA+中的2.2.1.1和2.2.1.2,onFulfilled和onRejected必須是函數,不然就會被忽略  
  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.   // 根據PromiseA+ 2.2.7規范 then函數必須返回一個promise對象  
  30.   return new PromiseCopy((resolve, reject) => {});  
  31. }; 

then 的額外實現

上面實現的 then 也是一個簡單的用法,不過根據 PromiseA+的規范這個 then 函數還有幾個點沒有實現,看代碼解釋 

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

思考

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

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

then 實現加強版 

  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不是一個函數resolve--self.info.value  
  17.           let value = self.info.value;  
  18.           // 這個注釋不要,留著只是為了記錄當時的思路  
  19.           // 這個加判斷是為了防止then函數逇回調不是一個函數,,是一個字符串  
  20.           //   if (typeof onFulfilled === "function") {  
  21.           //     value = onFulfilled(value);  
  22.           //   }  
  23.           value = onFulfilled(value);  
  24.           // 這里要做一個[[Resolve]](promise2, x)處理了  
  25.           // 因為resolve里面直接做了,所以直接調用,和網上的一些實現有點不一樣  
  26.           // 他們是提取了一個resolvePromise函數調用,我是直接調用了resolve  
  27.           resolve(value);  
  28.         } catch (e) {  
  29.           reject(e);  
  30.         }  
  31.       });  
  32.     }  
  33.     // 注意這里根據上面可知onFulfilled,onRejected拋出的值都要經過[[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狀態也需要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. }; 

小結

到這里 promise 的主體實現已經完成了,下面是測試結果

Promise 其他靜態方法

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

這個方法有幾個特點如下

  •  該方法接受一個數組,數組每一個元素都是一個 promise 對象
  •  只有所有 promise 都是 onFulfilled 的時候才會執行 then 回調,并且結果順序和數組的一致
  •  如果其中一個 promise 發生了 reject 那么就會返回這個值 
  1. PromiseCopy.all = function (data) {  
  2.   let count = 0; // 記錄調用次數  
  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

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

  •  這個方法也是接受數組,數組的元素是 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.   // 而且狀態一定是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. // 后來發現catch有一個簡單的實現方法  
  38. // 沒有刪除上面就是為了記錄思路過程  
  39. Promise.prototype.catch = function (onRejected) {  
  40.   return this.then(null, onRejected);  
  41. }; 

deferred

這個是 Promise 提供的一個快捷使用,自己實現 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 源碼已經上傳到個人 github ( https://github.com/yizhengfeng-jj/promise 歡迎 star) 

 

責任編輯:龐桂玉 來源: 前端大全
相關推薦

2025-03-17 00:21:00

2022-02-11 13:44:56

fiber架構React

2021-04-07 20:01:23

Go變量常量

2025-04-21 04:00:00

2020-04-28 22:12:30

Nginx正向代理反向代理

2022-04-25 09:03:16

JavaScript代碼

2022-11-11 08:19:03

redis分布式

2017-07-20 16:55:56

Android事件響應View源碼分析

2022-03-26 08:49:13

MySQL數據存儲

2017-12-05 17:44:31

機器學習CNN卷積層

2020-10-14 08:50:38

搞懂 Netty 線程

2025-05-06 01:14:00

系統編程響應式

2025-06-30 00:32:43

策略模式算法MyBatis

2024-01-03 13:39:00

JS,Javascrip算法

2023-10-18 10:55:55

HashMap

2025-04-11 05:55:00

2025-01-13 16:00:00

服務網關分布式系統架構

2024-06-21 08:32:24

2019-06-24 05:05:40

緩沖池查詢數據InnoDB

2019-06-26 09:41:44

分布式事務微服務
點贊
收藏

51CTO技術棧公眾號

主站蜘蛛池模板: 国产美女在线精品免费 | 国产成人福利在线 | 91一区二区三区在线观看 | 日本一区二区高清不卡 | 九九精品网 | 一区二区三区国产精品 | 亚洲欧美视频 | 青青草久久 | 乱一性一乱一交一视频a∨ 色爱av | 色婷婷影院 | 成人在线观看免费爱爱 | 成人福利网站 | 国产成人免费网站 | 精品av久久久久电影 | 国产精品视频网站 | 久久亚洲国产 | 婷婷成人在线 | 91视视频在线观看入口直接观看 | 久久精品国产久精国产 | 日韩av在线播 | 不卡一区二区三区四区 | 妹子干综合 | 中文字幕中文字幕 | 久久精品免费观看 | 一区二区三区视频在线观看 | 久久国产欧美日韩精品 | 岛国视频| 91精品国产91久久久久福利 | 欧美精品一区二区三区在线播放 | 国产精品欧美一区二区三区不卡 | 91视频一区二区 | 国产精品视频在线播放 | 欧美精品 在线观看 | 国产精品一区二区三区久久久 | 超碰在线观看97 | 亚洲激情在线观看 | 成人国产精品色哟哟 | 久久国内 | 中文字幕亚洲区一区二 | 日韩视频在线一区二区 | 无码日韩精品一区二区免费 |