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

          這篇手寫(xiě) Promise 你一定要康康

          共 11598字,需瀏覽 24分鐘

           ·

          2022-04-30 13:08

          最近重溫了一下 Q/Promise[1] 的設(shè)計(jì)講解,結(jié)合自己的理解和一些小優(yōu)化,決定也來(lái)寫(xiě)一篇手寫(xiě) Promise 的文章。本文的內(nèi)容適合對(duì) Promise 的使用有一定了解的童鞋,因?yàn)檫^(guò)程中不會(huì)過(guò)多解釋 Promise 的基礎(chǔ)操作。我們從一個(gè)基礎(chǔ)版本開(kāi)始,漸進(jìn)式地完成這個(gè) Promise,在過(guò)程中分享我的理解和觀點(diǎn)。內(nèi)容可能有點(diǎn)長(zhǎng),廢話不多說(shuō),我們開(kāi)始吧。

          基礎(chǔ)版本

          我們先以觀察者模式作為基石來(lái)搭建一個(gè)基礎(chǔ)版本,實(shí)現(xiàn)的功能如下:

          1. 構(gòu)造函數(shù)接受一個(gè)函數(shù) `exector` 作為參數(shù),該函數(shù)的第一個(gè)參數(shù)是 `resolve`,作用是把 Promise 對(duì)象的狀態(tài)變?yōu)椤俺晒Α薄?br>
          2. 原型方法 `then` 是用來(lái)注冊(cè)一個(gè)當(dāng)狀態(tài)變?yōu)槌晒Φ幕卣{(diào)函數(shù),當(dāng)回調(diào)觸發(fā)時(shí),參數(shù)是 `resolve` 時(shí)的決議值。
          function?Promise(exector)?{
          ??this.pending?=?[];
          ??this.value?=?undefined;

          ??const?resolve?=?value?=>?{
          ????if?(this.pending)?{
          ??????this.value?=?value;
          ??????for?(const?onFulfilled?of?this.pending)?{
          ????????//?通知觀察者。
          ????????onFulfilled(this.value);
          ??????}
          ??????this.pending?=?undefined;
          ????}
          ??};

          ??exector(resolve);
          }

          Promise.prototype.then?=?function?(onFulfilled)?{
          ??if?(this.pending)?{
          ????//?還沒(méi)決議,先注冊(cè)觀察者。
          ????this.pending.push(onFulfilled);
          ??}?else?{
          ????//?已決議,直接通知。
          ????onFulfilled(this.value);
          ??}
          };

          //?測(cè)試一下。
          const?p?=?new?Promise(resolve?=>?{
          ??setTimeout(()?=>?resolve(666),?100);
          })

          p.then(res?=>?console.log('res:?%s',?res));

          //?輸出:
          //?res:?666
          復(fù)制代碼

          代碼很簡(jiǎn)單,應(yīng)該不用過(guò)多解釋?zhuān)厦娴耐暾a在這里:p0.js[2]

          這個(gè)基礎(chǔ)版本有個(gè)明顯的問(wèn)題:then 不能進(jìn)行鏈?zhǔn)秸{(diào)用,接著就來(lái)優(yōu)化一下。

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

          then 的鏈?zhǔn)秸{(diào)用會(huì)返回一個(gè)新的 Promise,并且 then 中回調(diào)的返回值會(huì)使這個(gè)新的 Promise 決議為“成功”狀態(tài)。

          Promise.prototype.then?=?function?(onFulfilled)?{
          ??//?“當(dāng)前”P(pán)romise,對(duì)于返回的新 Promise 而言,也是“前一個(gè)”P(pán)romise。
          ??const?prev?=?this;

          ??const?promise?=?new?Promise(resolve?=>?{
          ????//?包裝 onFulfilled,使其可以“傳播”決議;
          ????//?“前一個(gè)” Promise 決議后,決議返回的這個(gè)新 Promise。
          ????const?onSpreadFulfilled?=?function?(value)?{
          ??????resolve(onFulfilled(value));
          ????};

          ????if?(prev.pending)?{
          ??????prev.pending.push(onSpreadFulfilled);
          ????}?else?{
          ??????onSpreadFulfilled(prev.value);
          ????}
          ??});

          ??return?promise;
          };

          //?測(cè)試一下。
          const?p?=?new?Promise(resolve?=>?{
          ??setTimeout(()?=>?resolve(666),?100);
          });

          p.then(res?=>?{
          ??console.log('res1:?%s',?res);
          ??return?res?+?1;
          ).then(res?=>?{
          ??console.log('res2:?%s',?res);
          );
          ??
          //?輸出:
          //?res1:?666
          //?res2:?667
          復(fù)制代碼

          實(shí)現(xiàn)鏈?zhǔn)秸{(diào)用的關(guān)鍵是如何決議返回的新 Promise?這里我對(duì)變量做了一些有含義的命名,方便理解:

          1. prev 是調(diào)用 then 時(shí)“當(dāng)前”的 Promise,對(duì)于返回的新 Promise 而言,可以看做是“前一個(gè)”P(pán)romise。
          2. 包裝 onFulfilled——執(zhí)行完當(dāng)前注冊(cè)的 onFulfilled 后,用其返回值來(lái)決議返回的那個(gè)新的 Promise。這是個(gè)關(guān)鍵步驟,為體現(xiàn)傳播的動(dòng)作,將其命名為 onSpreadFulfilled
          3. onSpreadFulfilled 作為成功的回調(diào)注冊(cè)到 prev 上。

          上面的完整代碼在這里:p1.js[3]

          現(xiàn)在又有個(gè)新問(wèn)題,如果 resolvevalue 是個(gè) Promise,或者 onfulfilled 函數(shù)返回的結(jié)果是個(gè) Promise,那么鏈?zhǔn)絺鞑サ臎Q議值不應(yīng)該是這個(gè) Promise 本身,而是這個(gè) Promise 的決議值才對(duì),也就是要支持 Promise 的狀態(tài)傳遞

          狀態(tài)傳遞

          在實(shí)現(xiàn)狀態(tài)傳遞之前,我們先來(lái)康康如何確定一個(gè)值是不是 Promise。我們可以用原型繼承來(lái)判斷:

          return?value?instanceof?Promise;
          復(fù)制代碼

          這樣的缺點(diǎn)是兼容性較差,你無(wú)法強(qiáng)制使用者的運(yùn)行環(huán)境上下文中只會(huì)用一種 Promise 的庫(kù),或者在不同的運(yùn)行上下文中傳遞 Promise 實(shí)例。所以這里我們使用 鴨子類(lèi)型[4] 來(lái)判斷 Promise,重點(diǎn)關(guān)注對(duì)象的行為,將 Promise 看作是一個(gè) thenable 對(duì)象。

          function?isPromise(value)?{
          ??//?如果這個(gè)對(duì)象上可以調(diào)用 then 方法,就認(rèn)為它是一個(gè)“Promise”了。
          ??return?value?&&?typeof?value.then?===?'function';
          }
          復(fù)制代碼

          接下來(lái)就來(lái)實(shí)現(xiàn)狀態(tài)傳遞了,實(shí)現(xiàn)的思路就是基于鴨子類(lèi)型和“通知轉(zhuǎn)移”。我們先定義一個(gè)函數(shù):

          function?wrapToThenable(value)?{
          ??if?(isPromise(value))?{
          ????return?value;
          ??}?else?{
          ????return?{
          ??????then:?function?(onFulfilled)?{
          ????????return?wrapToThenable(onFulfilled(value));
          ??????}
          ????};
          ??}
          }
          復(fù)制代碼

          顧名思義,這個(gè)函數(shù)的作用是用來(lái)把一個(gè)值包裝為 thenable 對(duì)象:如果 value 是 Promise 則直接返回;如果不是就包裝并返回一個(gè)有 then 方法的對(duì)象,也就是 thenable 對(duì)象。這個(gè) thenable 對(duì)象的作用是啥呢?接著看這里:

          function?Promise(exector)?{
          ??this.pending?=?[];
          ??this.value?=?undefined;

          ??const?resolve?=?value?=>?{
          ????if?(this.pending)?{
          ??????//?包裝為 thenable。
          ??????this.value?=?wrapToThenable(value);
          ??????for?(const?onFulfilled?of?this.pending)?{
          ????????//?通知時(shí)改為調(diào)用 thenable 上的 then。
          ????????this.value.then(onFulfilled);
          ??????}
          ??????this.pending?=?undefined;
          ????}
          ??};

          ??exector(resolve);
          }
          復(fù)制代碼

          resolve 決議時(shí),根據(jù) value 的類(lèi)型不同,有兩種處理情況:

          1. 如果 value 是普通值,經(jīng)過(guò) wrapToThenable 會(huì)包裝為 thenable 對(duì)象,通知時(shí)調(diào)用 then 方法相當(dāng)于直接調(diào)用 onFulfilled
          2. 如果 value 是 Promise,則把 onFulfilled 注冊(cè)到 value 上;等到 value 決議時(shí),就會(huì)調(diào)用 onFulfilled。還記得鏈?zhǔn)秸{(diào)用時(shí)的 onSpreadFulfilled 嗎?這里就是“通知轉(zhuǎn)移”了,把通知下一個(gè) Promise 的責(zé)任轉(zhuǎn)移到了 value 身上。

          當(dāng)然 then 也要做一點(diǎn)修改:

          Promise.prototype.then?=?function?(onFulfilled)?{
          ??const?prev?=?this;

          ??const?promise?=?new?Promise(resolve?=>?{
          ????const?onSpreadFulfilled?=?function?(value)?{
          ??????resolve(onFulfilled(value));
          ????};

          ????if?(prev.pending)?{
          ??????prev.pending.push(onSpreadFulfilled);
          ????}?else?{
          ??????//?這里也要改為調(diào)用 then。
          ??????prev.value.then(onSpreadFulfilled);
          ????}
          ??});

          ??return?promise;
          };

          //?測(cè)試一下。
          const?p?=?new?Promise(resolve?=>?{
          ??setTimeout(()?=>?resolve(666),?100);
          });

          p.then(res?=>?{
          ??console.log('res1:?%s',?res);
          ??return?new?Promise(resolve?=>?{
          ????setTimeout(()?=>?resolve(777),?100);
          ??});
          }).then(res?=>?{
          ??console.log('res2:?%s',?res);
          });

          //?輸出:
          //?res1:?666
          //?res2:?777
          復(fù)制代碼

          這里來(lái)總結(jié)一下?tīng)顟B(tài)傳遞的設(shè)計(jì)思路。包裝為 thenable 對(duì)象非常關(guān)鍵,作用是保持了與 Promise 一致的行為,也就是接口一致。這樣在 resolve 時(shí)我們不用特定去判斷這個(gè)值是不是 Promise,而可以用統(tǒng)一的處理方式來(lái)通知觀察者;并且也順便完成了“通知轉(zhuǎn)移”,如果 value 還沒(méi)有決議,則 then 會(huì)注冊(cè)為回調(diào),如果已決議則 then 會(huì)立即執(zhí)行。

          上面的完整代碼在這里:p2.js[5]。接下來(lái),我們來(lái)完善一下 reject

          失敗狀態(tài)

          當(dāng) Promise 決議失敗時(shí),then 方法里面將只執(zhí)行第二個(gè)參數(shù) onRejected 對(duì)應(yīng)的回調(diào)。首先我們需要另一個(gè)包裝函數(shù):

          function?wrapToRejected(value)?{
          ??return?{
          ????then:?function?(_,?onRejected)?{
          ??????return?wrapToThenable(onRejected(value));
          ????}
          ??};
          }
          復(fù)制代碼

          這個(gè)函數(shù)的作用是一旦發(fā)生 reject(value) 時(shí),我們把 value 變?yōu)榱硪环N thenable 對(duì)象,這個(gè)對(duì)象在執(zhí)行 then 時(shí)只會(huì)調(diào)用 onRejected

          然后改變一下構(gòu)造函數(shù):

          function?Promise(exector)?{
          ??// pending 變?yōu)橐粋€(gè)二維數(shù)組,里面存放的元素是?[onFulfilled, onRejected]。
          ??this.pending?=?[];
          ??this.value?=?undefined;

          ??const?resolve?=?value?=>?{
          ????if?(this.pending)?{
          ??????this.value?=?wrapToThenable(value);
          ??????for?(const?handlers?of?this.pending)?{
          ????????this.value.then.apply(this.value,?handlers);
          ??????}
          ??????this.pending?=?undefined;
          ????}
          ??};

          ??const?reject?=?value?=>?{
          ????resolve(wrapToRejected(value));
          ??};

          ??exector(resolve,?reject);
          }
          復(fù)制代碼

          現(xiàn)在有一個(gè)比較大的變化:this.pending 變?yōu)榱硕S數(shù)組。這樣 this.value.then.apply 在執(zhí)行時(shí)會(huì)有三種情況:

          1. this.value 是成功決議轉(zhuǎn)換來(lái)的 thenable 對(duì)象,還記得 wrapToThenable 嗎?then 被執(zhí)行時(shí)只會(huì)調(diào)用 onFulfilled
          2. this.value 是失敗決議轉(zhuǎn)換來(lái)的 thenable 對(duì)象,then 被執(zhí)行時(shí)只會(huì)調(diào)用 onRejected
          3. this.value 是一個(gè) Promise,決議會(huì)轉(zhuǎn)移到這個(gè) Promise 上。

          同樣 then 方法也要做一些修改:

          Promise.prototype.then?=?function?(onFulfilled,?onRejected)?{
          ??const?prev?=?this;
          ??
          ??//?注意這里給了 onFulfilled、onRejected 默認(rèn)值。
          ??onFulfilled?=
          ????onFulfilled?||
          ????function?(value)?{
          ??????return?value;
          ????};
          ??onRejected?=
          ????onRejected?||
          ????function?(value)?{
          ??????return?wrapToRejected(value);
          ????};

          ??const?promise?=?new?Promise(resolve?=>?{
          ????const?onSpreadFulfilled?=?function?(value)?{
          ??????resolve(onFulfilled(value));
          ????};
          ????const?onSpreadRejected?=?function?(value)?{
          ??????resolve(onRejected(value));
          ????};

          ????if?(prev.pending)?{
          ??????prev.pending.push([onSpreadFulfilled,?onSpreadRejected]);
          ????}?else?{
          ??????prev.value.then(onSpreadFulfilled,?onSpreadRejected);
          ????}
          ??});

          ??return?promise;
          };

          //?測(cè)試一下。
          const?p?=?new?Promise((resolve,?reject)?=>?{
          ??setTimeout(()?=>?reject(666),?100);
          });

          p.then(undefined,?err?=>?{
          ??console.log('err1:?%s',?err);
          ??return?1;
          }).then(res?=>?{
          ??console.log('res1:?%s',?res);
          });

          //?輸出:
          //?err1:?666
          //?res1:?1
          復(fù)制代碼

          我們要特別注意一下增加了 onFulfilledonRejected 的默認(rèn)值。在實(shí)際使用 then 時(shí),可能只會(huì)專(zhuān)注處理成功或者失敗的回調(diào),但是我們又需要另外一種狀態(tài)要繼續(xù)傳播下去。這里可能有點(diǎn)不好理解,可以代入數(shù)據(jù)模擬一下。上面的完整代碼在這里:p3.js[6]

          又到了思考總結(jié)時(shí)間,thenable 這個(gè)接口是關(guān)鍵所在。通過(guò)兩個(gè)包裝對(duì)象,分別處理成功和失敗的狀態(tài),在通知觀察者時(shí)可以保持統(tǒng)一的邏輯,這個(gè)設(shè)計(jì)是不是感覺(jué)很妙呢?

          接下來(lái)我們要處理一下調(diào)用時(shí)會(huì)產(chǎn)生異常的問(wèn)題。

          異常處理

          我們先思考一下會(huì)有哪些地方會(huì)產(chǎn)生異常?第一個(gè)是構(gòu)造函數(shù)里面 exector 執(zhí)行的時(shí)候:

          function?Promise(exector)?{
          ??this.pending?=?[];
          ??this.value?=?undefined;

          ??const?resolve?=?value?=>?{
          ????//?...
          ??};

          ??const?reject?=?value?=>?{
          ????resolve(wrapToRejected(value));
          ??};

          ??try?{
          ????exector(resolve,?reject);
          ??}?catch?(e)?{
          ????//?如果有異常產(chǎn)生,狀態(tài)變?yōu)椤笆 薄?/span>
          ????reject(e);
          ??}
          }
          復(fù)制代碼

          然后是onFulfilledonRejected 執(zhí)行的時(shí)候。當(dāng)在以上兩個(gè)方法里產(chǎn)生異常時(shí),狀態(tài)要變?yōu)槭。⑶倚枰旬惓鞑ハ氯ァ?code style="font-size: 14px;padding: 2px 4px;border-radius: 4px;margin-right: 2px;margin-left: 2px;background-color: rgba(27, 31, 35, 0.05);font-family: "Operator Mono", Consolas, Monaco, Menlo, monospace;word-break: break-all;color: rgb(239, 112, 96);">then 的改動(dòng)如下:

          Promise.prototype.then?=?function?(onFulfilled,?onRejected)?{
          ??//?...
          ??//?產(chǎn)生異常的時(shí)候包裝一下。
          ??const?errHandler?=?returnWhenError(err?=>?wrapToRejected(err));
          ??onFulfilled?=?errHandler(onFulfilled);
          ??onRejected?=?errHandler(onRejected);

          ??const?promise?=?new?Promise(resolve?=>?{
          ????const?onSpreadFulfilled?=?function?(value)?{
          ??????resolve(onFulfilled(value));
          ????};
          ????const?onSpreadRejected?=?function?(value)?{
          ??????resolve(onRejected(value));
          ????};

          ????if?(prev.pending)?{
          ??????prev.pending.push([onSpreadFulfilled,?onSpreadRejected]);
          ????}?else?{
          ??????prev.value.then(onSpreadFulfilled,?onSpreadRejected);
          ????}
          ??});

          ??return?promise;
          };

          //?封裝為一個(gè)可重用的高階函數(shù)。
          //?如果 fun 執(zhí)行失敗了,則返回 onError 的結(jié)果。
          function?returnWhenError(onError)?{
          ??return?fun?=>
          ????(...args)?=>?{
          ??????let?result;

          ??????try?{
          ????????result?=?fun(...args);
          ??????}?catch?(e)?{
          ????????result?=?onError(e);
          ??????}

          ??????return?result;
          ????};
          }
          復(fù)制代碼

          然后我們可以加入 catch 方法:

          Promise.prototype.catch?=?function?(onRejected)?{
          ??//?在 then 中忽略掉“成功”狀態(tài)的回調(diào)。
          ??return?Promise.prototype.then.call(this,?undefined,?onRejected);
          };

          //?測(cè)試一下。
          const?p?=?new?Promise(resolve?=>?{
          ??setTimeout(()?=>?resolve(666),?100);
          });

          p.then(res?=>?{
          ??console.log('res1:?%s',?res);
          ??throw?new?Error('test?error1');
          }).then(undefined,?err?=>?{
          ??console.log('err1:?%s',?err.message);
          ??throw?new?Error('test?error2');
          }).catch(err?=>?{
          ??console.log('err2:?%s',?err.message);
          });

          //?輸出:
          //?res1:?666
          //?err1:?test?error1
          //?err2:?test?error2
          復(fù)制代碼

          上面的完整代碼在這里:p4.js[7]

          到了這里,基本上 Promise 的基本功能就差不多完成了。不過(guò)還有一些不太完善的地方,我們來(lái)繼續(xù)做一些優(yōu)化。

          一些優(yōu)化

          封裝私有變量

          this.pendingthis.value 從外部是可以讀寫(xiě)的,不夠安全和健壯。而我又還是想用構(gòu)造函數(shù)和原型方法,不想用閉包來(lái)封裝。我這里采用的是 WeakMap[8] 來(lái)達(dá)到目的,關(guān)鍵的修改如下:

          const?refMap?=?new?WeakMap();

          //?...

          function?Promise(exector)?{
          ??//?用當(dāng)前的實(shí)例引用作為 key,把想隱藏的數(shù)據(jù)放進(jìn)一個(gè)對(duì)象里。
          ??refMap.set(this,?{
          ????pending:?[],
          ????value:?undefined
          ??});

          ??const?resolve?=?value?=>?{
          ????//?取出封裝的數(shù)據(jù)。
          ????const?data?=?refMap.get(this);

          ????if?(data.pending)?{
          ??????data.value?=?wrapToThenable(value);
          ??????for?(const?handlers?of?data.pending)?{
          ????????data.value.then.apply(data.value,?handlers);
          ??????}
          ??????data.pending?=?undefined;
          ????}
          ??};

          ??//?...
          }
          復(fù)制代碼

          同樣 then 也修改一下:

          Promise.prototype.then?=?function?(onFulfilled,?onRejected)?{
          ??//?...

          ??const?promise?=?new?Promise(resolve?=>?{
          ????const?onSpreadFulfilled?=?function?(value)?{
          ??????resolve(onFulfilled(value));
          ????};
          ????const?onSpreadRejected?=?function?(value)?{
          ??????resolve(onRejected(value));
          ????};
          ????//?取出封裝的數(shù)據(jù)。
          ????const?data?=?refMap.get(prev);

          ????if?(data.pending)?{
          ??????data.pending.push([onSpreadFulfilled,?onSpreadRejected]);
          ????}?else?{
          ??????data.value.then(onSpreadFulfilled,?onSpreadRejected);
          ????}
          ??});

          ??return?promise;
          };
          復(fù)制代碼

          上面的完整代碼在這里:p5.js[9]

          當(dāng) Promise 實(shí)例被垃圾回收時(shí),對(duì)應(yīng)在 WeakMap 中的私有數(shù)據(jù)對(duì)象引用也會(huì)被消除,沒(méi)有內(nèi)存泄漏問(wèn)題,這種方案非常適合用來(lái)封裝私有變量。

          調(diào)用順序

          目前的 Promise 在執(zhí)行時(shí)有調(diào)用順序問(wèn)題,比如:

          const?p?=?new?Promise(resolve?=>?resolve(1));

          p.then(res?=>?{
          ??console.log('res1:',?res);
          ??return?res?+?1;
          }).then(res?=>?{
          ??console.log('res2:',?res);
          });

          p.then(res?=>?{
          ??console.log('res3:',?res);
          });

          console.log('Hi!');

          //?目前的輸出是:
          //?res1:?1
          //?res2:?2
          //?res3:?1
          //?Hi!

          //?正確的輸出應(yīng)該是:
          //?Hi!
          //?res1:?1
          //?res3:?1
          //?res2:?2
          復(fù)制代碼

          一個(gè)簡(jiǎn)單的做法是利用 setTimeout 來(lái)改進(jìn):

          function?Promise(exector)?{
          ??//?...
          ??
          ??const?resolve?=?value?=>?{
          ????const?data?=?refMap.get(this);

          ????if?(data.pending)?{
          ??????data.value?=?wrapToThenable(value);
          ??????for?(const?handlers?of?data.pending)?{
          ????????//?延遲執(zhí)行。
          ????????enqueue(()?=>?{
          ??????????data.value.then.apply(data.value,?handlers);
          ????????});
          ??????}
          ??????data.pending?=?undefined;
          ????}
          ??};
          ??
          ??//?...
          }

          Promise.prototype.then?=?function?(onFulfilled,?onRejected)?{
          ??//?...

          ??const?promise?=?new?Promise(resolve?=>?{
          ????//?...

          ????if?(data.pending)?{
          ??????data.pending.push([onSpreadFulfilled,?onSpreadRejected]);
          ????}?else?{
          ??????//?延遲執(zhí)行。
          ??????enqueue(()?=>?{
          ????????data.value.then(onSpreadFulfilled,?onSpreadRejected);
          ??????});
          ????}
          ??});

          ??return?promise;
          };

          function?enqueue(callback)?{
          ??setTimeout(callback,?1);
          }
          復(fù)制代碼

          enqueue 的作用是模擬按入隊(duì)順序來(lái)延遲執(zhí)行函數(shù)。通過(guò)對(duì)所有 then 調(diào)用的延遲執(zhí)行,可以保證按正確的注冊(cè)順序和決議順序來(lái)執(zhí)行了,上面的完整代碼在這里:p6.js[10]

          接下來(lái)呢?

          咳咳,到了這里我覺(jué)得就先差不多了,畢竟此文的目的是分享和交流一種 Promise 的設(shè)計(jì)思路和心得,而不是去造一個(gè)完美的 Promise。手寫(xiě)一個(gè) Promise 這個(gè)結(jié)果不應(yīng)該是我們的目的,觀察演進(jìn)過(guò)程中的思路和方案才是我們需要吸收的東西。后面有時(shí)間我會(huì)把缺少的一些接口也補(bǔ)上,比如 Promise.resolvePromise.prototype.finally 等等。

          最后,希望你也能從這篇文章中收獲一些東西吧,歡迎 star 和關(guān)注我的 JavaScript 博客:小聲比比 JavaScript[11]

          關(guān)于本文

          作者:deepfunc

          https://juejin.cn/post/7085298532365631501

          最后


          歡迎關(guān)注【前端瓶子君】??ヽ(°▽°)ノ?
          回復(fù)「算法」,加入前端編程源碼算法群領(lǐng)取最新最熱的前端算法小書(shū)、面試小書(shū)以及海量簡(jiǎn)歷模板,期待與你共進(jìn)步!
          回復(fù)「交流」,吹吹水、聊聊技術(shù)、吐吐槽!
          回復(fù)「閱讀」,每日刷刷高質(zhì)量好文!
          如果這篇文章對(duì)你有幫助,在看」是最大的支持
          ?》》面試官也在看的算法資料《《
          “在看和轉(zhuǎn)發(fā)”就是最大的支持



          瀏覽 24
          點(diǎn)贊
          評(píng)論
          收藏
          分享

          手機(jī)掃一掃分享

          分享
          舉報(bào)
          評(píng)論
          圖片
          表情
          推薦
          點(diǎn)贊
          評(píng)論
          收藏
          分享

          手機(jī)掃一掃分享

          分享
          舉報(bào)
          <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>
                  日韩 欧美 高清 | 国产一级片免费视频 | 欧美性爱天天 | 日本乱婬妺妺躁爽A片 | 成人欧美日韩 |