<kbd id="afajh"><form id="afajh"></form></kbd>
<strong id="afajh"><dl id="afajh"></dl></strong>
    <del id="afajh"><form id="afajh"></form></del>
        1. <th id="afajh"><progress id="afajh"></progress></th>
          <b id="afajh"><abbr id="afajh"></abbr></b>
          <th id="afajh"><progress id="afajh"></progress></th>

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

          共 13294字,需瀏覽 27分鐘

           ·

          2020-10-21 21:11

          作者:一陣風,一枚只想安靜寫代碼的程序員,來自程序員成長指北交流群? ??github: https://github.com/yizhengfeng-jj/promise

          前言

          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)了。

          const?PromiseCopy?=?function?(fn)?{
          ??this.info?=?{
          ????status:?"pending",
          ????value:?"",
          ??};

          ??const?self?=?this;

          ??self.onFulfilledArr?=?[];?//?then函數(shù)里面的第一個回調(diào)函數(shù)的集合
          ??self.onRejectedArr?=?[];?//?then函數(shù)里面的第二個回調(diào)函數(shù)的集合

          ??const?resolve?=?function?(value)?{
          ????//?加這個判斷是為了表示,只有在pendding狀態(tài)下才會去執(zhí)行
          ????//?狀態(tài)已經(jīng)變成onFulfilled之后就不能再去改變了
          ????//?符合PromiseA+中的2.1.2.1
          ????if?(self.info.status?===?"pending")?{
          ??????self.info.status?=?"onFulfilled";

          ??????self.info.value?=?value;

          ??????self.onFulfilledArr.forEach((fn)?=>?fn(value));
          ????}
          ??};

          ??//?和上面同理符合PromiseA+,2.1.3.1
          ??const?reject?=?function?(value)?{
          ????if?(self.info.status?===?"pending")?{
          ??????self.info.status?=?"onRejected";

          ??????self.info.value?=?value;

          ??????self.onRejectedArr.forEach((fn)?=>?fn(value));
          ????}
          ??};

          ??fn(resolve,?reject);
          };

          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ù)里面應該是要判斷上面幾點的,所以我們上面寫的代碼是有誤的
          • 還有一個問題是,我們需要在 resolve 函數(shù)里面判斷 x 是不是實例的本身,但是正常的 resolve 函數(shù)我們經(jīng)常是傳入一個參數(shù),所以中間肯定是有一個中間函數(shù)的,看下面的代碼
          const?PromiseCopy?=?function?(fn)?{
          ??this.info?=?{
          ????status:?"pending",
          ????value:?"",
          ??};

          ??const?self?=?this;

          ??self.onFulfilledArr?=?[];?//?then函數(shù)里面的第一個回調(diào)函數(shù)的集合
          ??self.onRejectedArr?=?[];?//?then函數(shù)里面的第二個回調(diào)函數(shù)的集合

          ??//?_resolve?是我們經(jīng)常調(diào)用的resolve
          ??//?但是真正實現(xiàn)的應該是里面的resolve
          ??const?_resolve?=?function?(value)?{
          ????//?這個函數(shù)得改變一下
          ????//?PromiseCopy一旦被實例化,那么self就是實例本身了
          ????resolve(self,?value);
          ??};

          ??//?此時我們就可以在resolve進行判斷了
          ??const?resolve?=?function?(promise,?value)?{
          ????let?ifexec?=?false;

          ????//?首先判斷value是不是promise本身
          ????if?(value?===?promise)?{
          ??????//?一定要用TypeError寫?不然promises-aplus-tests跑不通
          ??????//?切記這是第一個坑,promises-aplus-tests只認TypeError這種錯誤形式
          ??????reject(new?TypeError("A?promise?cannot?be?onFulfilled?with?itself."));
          ????}

          ????//?value是一個thenable對象
          ????//?這個要Object.prototype.toString.call(value)?===?"[object?Object]"判斷
          ????//?不然resolve([])有問題,不知道是不是我實現(xiàn)問題
          ????if?(
          ??????value?&&
          ??????(Object.prototype.toString.call(value)?===?"[object?Object]"?||
          ????????typeof?value?===?"function")
          ????)?{
          ??????//?var?promise1?=?Promise.resolve(dump).then(function?()?{
          ??????//???return?{
          ??????//?????then:?(resolve,?reject)?=>?{
          ??????//???????setTimeout(()?=>?{
          ??????//?????????resolve({
          ??????//???????????then:?(resolve,?reject)?=>?{
          ??????//?????????????resolve("aa111a");
          ??????//?????????????throw?"other";
          ??????//???????????},
          ??????//?????????});
          ??????//???????});
          ??????//?????},
          ??????//???};
          ??????//?});

          ??????//?promise1.then(
          ??????//???(res)?=>?{
          ??????//?????console.log(res?===?"aa111a");
          ??????//?????console.log("aaa");
          ??????//???},
          ??????//???(res)?=>?{
          ??????//?????console.log(res);
          ??????//?????console.log("error");
          ??????//???}
          ??????//?);

          ??????//?這里的try--catch一定要加?,不然會promises-aplus-tests會一直報錯,這是第三個大坑
          ??????//?因為promises-aplus-test測試里面有這一條的
          ??????//?看上面注釋例子

          ??????try?{
          ????????//?拿到then函數(shù)

          ????????const?then?=?value.then;

          ????????//?如果then是一個函數(shù)則執(zhí)行這個函數(shù)
          ????????if?(typeof?then?===?"function")?{
          ??????????//?為什么要.call(value,?x,?y)?你們可以自己試一下原生的Promise在這種情況下this指向的就是value,所以要綁定
          ??????????//?因為then我們已經(jīng)拿出來了then?=?value.then,直接調(diào)用then(),this就指向的window
          ??????????//?為什么后面還需要綁定兩個函數(shù)了
          ??????????//?根據(jù)原生的Promise可知,thenable中的then函數(shù)可以接受兩個函數(shù)resolve,reject
          ??????????//?只有手動調(diào)用了resolve和reject才會執(zhí)行后面的.then操作,具體大家自己操作下

          ??????????then.call(
          ????????????value,
          ????????????function?(value)?{
          ??????????????if?(ifexec)?{
          ????????????????return;
          ??????????????}

          ??????????????//?ifexec這個一定要加,不然也會報200ms錯誤,第四個大坑
          ??????????????//?目的是為了不讓多次執(zhí)行,語言無法表達看下面的例子
          ??????????????//?var?promise1?=?Promise.resolve(dump).then(function?()?{
          ??????????????//???return?{
          ??????????????//?????then:?(resolve,?reject)?=>?{
          ??????????????//???????resolve("aa111a");
          ??????????????//???????resolve("aa111a");
          ??????????????//?????},
          ??????????????//???};
          ??????????????//?});
          ??????????????ifexec?=?true;
          ??????????????resolve(promise,?value);
          ????????????},
          ????????????function?(value)?{
          ??????????????if?(ifexec)?{
          ????????????????return;
          ??????????????}

          ??????????????ifexec?=?true;
          ??????????????reject(value);
          ????????????}
          ??????????);

          ??????????return;
          ????????}
          ??????}?catch?(e)?{
          ????????if?(ifexec)?{
          ??????????return;
          ????????}

          ????????ifexec?=?true;
          ????????reject(e);
          ??????}
          ????}

          ????//?下面這一點非常的重要,是async,await?和一些插件比如saga的核心
          ????//?就是如果x是一個promise對象,那么then的執(zhí)行取決于x的狀態(tài)
          ????//?還有這一個判斷一定要放在這里,不要和上面的換?不然promises-aplus-tests會報一個超過200ms的錯誤,切記這是第二個坑
          ????if?(value?&&?value?instanceof?PromiseCopy?&&?value.then?===?promise.then)?{
          ??????//?將promise的onFulfilledArr給到value
          ??????//?但是還沒有那么簡單我們要明白兩點
          ??????//?如果value這個promise已經(jīng)不是pendding,我們給了他也沒有用,所以需要直接調(diào)用
          ??????if?(value.info.status?===?"pending")?{
          ????????value.onFulfilledArr?=?self.onFulfilledArr;
          ????????value.onRejectedArr?=?self.onRejectedArr;
          ??????}

          ??????//?如果value狀態(tài)是onFulfilled
          ??????if?(value.info.status?===?"onRejected")?{
          ????????self.info.value?=?value.info.value;

          ????????self.onRejectedArr.forEach((fn)?=>?fn(value.info.value));
          ??????}

          ??????//?如果value狀態(tài)是reject
          ??????if?(value.info.status?===?"onFulfilled")?{
          ????????self.info.value?=?value.info.value;

          ????????self.onFulfilledArr.forEach((fn)?=>?fn(value.info.value));
          ??????}

          ??????return;
          ????}

          ????//?如果是一個普通的值
          ????//?加這個判斷是為了表示,只有在pendding狀態(tài)下才會去執(zhí)行
          ????//?狀態(tài)已經(jīng)變成onFulfilled之后就不能再去改變了
          ????//?符合PromiseA+中的2.1.2.1
          ????if?(self.info.status?===?"pending")?{
          ??????self.info.status?=?"onFulfilled";

          ??????self.info.value?=?value;

          ??????self.onFulfilledArr.forEach((fn)?=>?fn(value));
          ????}
          ??};

          ??//?和上面同理符合PromiseA+,2.1.3.1
          ??//?reject沒有resolve那么多規(guī)則,比較簡單
          ??const?reject?=?function?(value)?{
          ????if?(self.info.status?===?"pending")?{
          ??????self.info.status?=?"onRejected";

          ??????self.info.value?=?value;

          ??????self.onRejectedArr.forEach((fn)?=>?fn(value));
          ????}
          ??};

          ??//?此時fn調(diào)用的是_reoslve
          ??//?這個try?catch主要是實現(xiàn)promiseCopy.prototype.catch
          ??try?{
          ????fn(_resolve,?reject);
          ??}?catch?(e)?{
          ????setTimeout(()?=>?{
          ??????self.onRejectedArr.forEach((fn)?=>?fn(e));
          ????});
          ??}
          };

          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)一遍,在代碼里面會寫詳細一點的注釋。

          PromiseCopy.prototype.then?=?function?(onFulfilled,?onRejected)?{
          ??const?self?=?this;
          ??//?這里要判斷下,如果PromiseCopy是resolve了那么就直接執(zhí)行onFulfilled
          ??if?(self.info.status?===?"onFulfilled")?{
          ????setTimeout(()?=>?{
          ??????onFulfilled(self.info.value);
          ????});
          ??}

          ??if?(self.info.status?===?"onRejected")?{
          ????setTimeout(()?=>?{
          ??????onRejected(self.info.value);
          ????});
          ??}

          ??//?根據(jù)PromiseA+中的2.2.1.1和2.2.1.2,onFulfilled和onRejected必須是函數(shù),不然就會被忽略
          ??if?(typeof?onFulfilled?===?"function")?{
          ????self.onFulfilledArr.push(()?=>?{
          ??????setTimeout(()?=>?{
          ????????onFulfilled(self.info.value);
          ??????});
          ????});
          ??}

          ??if?(typeof?onRejected?===?"function")?{
          ????self.onRejectedArr.push(()?=>?{
          ??????setTimeout(()?=>?{
          ????????onRejected(self.info.value);
          ??????});
          ????});
          ??}

          ??//?根據(jù)PromiseA+?2.2.7規(guī)范?then函數(shù)必須返回一個promise對象
          ??return?new?PromiseCopy((resolve,?reject)?=>?{});
          };

          then 的額外實現(xiàn)

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

          promise2 = promise1.then(onFulfilled, onRejected);

          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 是一個關鍵, 并且這個拋出的 promise 的執(zhí)行還得看 onFulfilled 和 onRejected 返回值,這一點當時寫 promise 的時候想了很久,不知道如何組織,后來實在想不出來,看了下網(wǎng)上很多文章,發(fā)現(xiàn)這些邏輯都是在 PromiseCopy 主體里面實現(xiàn)的。

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

          then 實現(xiàn)加強版

          PromiseCopy.prototype.then?=?function?(onFulfilled,?onRejected)?{
          ??const?self?=?this;

          ??//?這個一定要這么寫目的為了讓值傳遞
          ??onFulfilled?=?typeof?onFulfilled?===?"function"???onFulfilled?:?(val)?=>?val;

          ??//?這個一定要這么寫,一定要拋出一個錯throw?err
          ??onRejected?=
          ????typeof?onRejected?===?"function"
          ????????onRejected
          ??????:?(err)?=>?{
          ??????????throw?err;
          ????????};

          ??const?newPromise?=?new?PromiseCopy((resolve,?reject)?=>?{
          ????if?(self.info.status?===?"onFulfilled")?{
          ??????setTimeout(()?=>?{
          ????????try?{
          ??????????//?如果onFulfilled不是一個函數(shù)resolve--self.info.value

          ??????????let?value?=?self.info.value;

          ??????????//?這個注釋不要,留著只是為了記錄當時的思路
          ??????????//?這個加判斷是為了防止then函數(shù)逇回調(diào)不是一個函數(shù),,是一個字符串
          ??????????//???if?(typeof?onFulfilled?===?"function")?{
          ??????????//?????value?=?onFulfilled(value);
          ??????????//???}

          ??????????value?=?onFulfilled(value);

          ??????????//?這里要做一個[[Resolve]](promise2,?x)處理了
          ??????????//?因為resolve里面直接做了,所以直接調(diào)用,和網(wǎng)上的一些實現(xiàn)有點不一樣
          ??????????//?他們是提取了一個resolvePromise函數(shù)調(diào)用,我是直接調(diào)用了resolve
          ??????????resolve(value);
          ????????}?catch?(e)?{
          ??????????reject(e);
          ????????}
          ??????});
          ????}

          ????//?注意這里根據(jù)上面可知onFulfilled,onRejected拋出的值都要經(jīng)過[[Resolve]](promise2,?x)
          ????//?這和resolve,reject不一樣,promise中resolve才走[[Resolve]](promise2,?x),reject不走
          ????if?(self.info.status?===?"onRejected")?{
          ??????setTimeout(()?=>?{
          ????????try?{
          ??????????let?{?value?}?=?self.info;

          ??????????value?=?onRejected(self.info.value);

          ??????????resolve(value);
          ????????}?catch?(e)?{
          ??????????reject(e);
          ????????}
          ??????});
          ????}

          ????//?如果是pending狀態(tài)也需要push
          ????if?(self.info.status?===?"pending")?{
          ??????self.onFulfilledArr.push((data)?=>?{
          ????????setTimeout(()?=>?{
          ??????????try?{
          ????????????let?value?=?data;
          ????????????value?=?onFulfilled(value);

          ????????????resolve(value);
          ??????????}?catch?(e)?{
          ????????????reject(e);
          ??????????}
          ????????});
          ??????});

          ??????self.onRejectedArr.push((data)?=>?{
          ????????setTimeout(()?=>?{
          ??????????try?{
          ????????????let?value?=?data;

          ????????????value?=?onRejected(data);

          ????????????resolve(value);
          ??????????}?catch?(e)?{
          ????????????reject(e);
          ??????????}
          ????????});
          ??????});
          ????}
          ??});

          ??return?newPromise;
          };

          小結

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

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

          Promise.resolve

          PromiseCopy.resolve?=?function?(data)?{
          ??return?new?PromiseCopy((resolve,?reject)?=>?{
          ????resolve(data);
          ??});
          };

          reject

          Promise.reject?=?function?(reason)?{
          ??return?new?Promise((resolve,?reject)?=>?{
          ????reject(reason);
          ??});
          };

          Promise.all

          這個方法有幾個特點如下

          • 該方法接受一個數(shù)組,數(shù)組每一個元素都是一個 promise 對象
          • 只有所有 promise 都是 onFulfilled 的時候才會執(zhí)行 then 回調(diào),并且結果順序和數(shù)組的一致
          • 如果其中一個 promise 發(fā)生了 reject 那么就會返回這個值
          PromiseCopy.all?=?function?(data)?{
          ??let?count?=?0;?//?記錄調(diào)用次數(shù)
          ??let?total?=?data.length;
          ??let?result?=?[];

          ??return?new?PromiseCopy((resolve,?reject)?=>?{
          ????for?(let?i?=?0;?i???????data[i].then(
          ????????(res)?=>?{
          ??????????result.push(res);

          ??????????++count;

          ??????????if?(count?===?totla)?{
          ????????????resolve(result);
          ??????????}
          ????????},
          ????????(res)?=>?{
          ??????????return?reject(res);
          ????????}
          ??????);
          ????}
          ??});
          };

          Promise.race

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

          • 這個方法也是接受數(shù)組,數(shù)組的元素是 promise
          • 他只返回最快的那一個 promise 的值
          • 就算有錯誤也會返回最快那一個 promise 的值
          PromiseCopy.race?=?function?(data)?{
          ??const?total?=?data.length;

          ??return?new?PromiseCopy((resolve,?reject)?=>?{
          ????for?(let?i?=?0;?i???????data[i].then(
          ????????(res)?=>?{
          ??????????resolve(res);
          ????????},
          ????????(res)?=>?{
          ??????????return?reject(res);
          ????????}
          ??????);
          ????}
          ??});
          };

          catch 方法

          PromiseCopy.prototype.catch?=?function?(onRejected)?{
          ??//?能到catch里面來的一定是走的reject的
          ??//?而且狀態(tài)一定是pendding
          ??const?self?=?this;
          ??const?newPromise?=?new?PromiseCopy((resolve,?reject)?=>?{
          ????if?(self.info.status?===?"onRejected")?{
          ??????try?{
          ????????setTimeout(()?=>?{
          ??????????let?{?value?}?=?self.info;

          ??????????if?(typeof?onRejected?===?"function")?{
          ????????????value?=?onRejected(self.info.value);
          ??????????}

          ??????????resolve(value);
          ????????});
          ??????}?catch?(e)?{
          ????????rejetc(e);
          ??????}
          ????}

          ????if?(self.info.status?===?"pending")?{
          ??????self.onRejectedArr.push((data)?=>?{
          ????????setTimeout(()?=>?{
          ??????????try?{
          ????????????let?value?=?data;

          ????????????if?(typeof?onRejected?===?"function")?{
          ??????????????value?=?onRejected(data);
          ????????????}

          ????????????resolve(value);
          ??????????}?catch?(e)?{
          ????????????reject(e);
          ??????????}
          ????????});
          ??????});
          ????}
          ??});

          ??return?newPromise;
          };

          //?后來發(fā)現(xiàn)catch有一個簡單的實現(xiàn)方法
          //?沒有刪除上面就是為了記錄思路過程
          Promise.prototype.catch?=?function?(onRejected)?{
          ??return?this.then(null,?onRejected);
          };

          deferred

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

          PromiseCopy.defer?=?PromiseCopy.deferred?=?function?()?{
          ??let?dfd?=?{};
          ??dfd.promise?=?new?PromiseCopy((resolve,?reject)?=>?{
          ????dfd.resolve?=?resolve;
          ????dfd.reject?=?reject;
          ??});
          ??return?dfd;
          };

          源碼

          promise 源碼已經(jīng)上傳到個人 github ( https://github.com/yizhengfeng-jj/promise 歡迎 star)

          參考文章

          解讀Promise內(nèi)部實現(xiàn)原理

          Promise 源碼分析

          小邵教你玩轉(zhuǎn)promise源碼

          ??愛心三連擊

          1.看到這里了就點個在看支持下吧,你的點贊在看是我創(chuàng)作的動力。

          2.關注公眾號程序員成長指北,回復「1」加入Node進階交流群!「在這里有好多 Node 開發(fā)者,會討論 Node 知識,互相學習」!

          3.也可添加微信【ikoala520】,一起成長。


          “在看轉(zhuǎn)發(fā)”是最大的支持

          瀏覽 39
          點贊
          評論
          收藏
          分享

          手機掃一掃分享

          分享
          舉報
          評論
          圖片
          表情
          推薦
          點贊
          評論
          收藏
          分享

          手機掃一掃分享

          分享
          舉報
          <kbd id="afajh"><form id="afajh"></form></kbd>
          <strong id="afajh"><dl id="afajh"></dl></strong>
            <del id="afajh"><form id="afajh"></form></del>
                1. <th id="afajh"><progress id="afajh"></progress></th>
                  <b id="afajh"><abbr id="afajh"></abbr></b>
                  <th id="afajh"><progress id="afajh"></progress></th>
                  黄色录像一级免费播放 | 免费成人视频久久 | 北条麻妃视频在线观看 | 拉拉免费观看视频大全高清 | 国产农村乱╳╳╳乱免费下载 |