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

          面試官:請手寫一個帶取消功能的延遲函數(shù),axios 取消功能的原理是什么

          共 9092字,需瀏覽 19分鐘

           ·

          2021-12-31 00:50

          本文倉庫 https://github.com/lxchuan12/delay-analysis.git,求個star^_^[1]

          源碼共讀活動 每周一期,已進(jìn)行到17期。于是搜尋各種值得我們學(xué)習(xí),且代碼行數(shù)不多的源碼。delay 主文件僅70多行[2],非常值得我們學(xué)習(xí)。

          閱讀本文,你將學(xué)到:

          1.?學(xué)會如何實現(xiàn)一個比較完善的?delay?函數(shù)
          2.?學(xué)會使用?AbortController?實現(xiàn)取消功能
          3.?學(xué)會面試常考?axios?取消功能實現(xiàn)
          4.?等等

          2. 環(huán)境準(zhǔn)備

          #?推薦克隆我的項目,保證與文章同步
          git?clone?https://github.com/lxchuan12/delay-analysis.git
          #?npm?i?-g?yarn
          cd?delay-analysis/delay?&&?yarn?i
          #?VSCode?直接打開當(dāng)前項目
          #?code?.
          #?我寫的例子都在?examples?這個文件夾中,可以啟動服務(wù)本地查看調(diào)試
          #?在?delay-analysis?目錄下
          npx?http-server?examples
          #?打開?http://localhost:8080

          #?或者克隆官方項目
          git?clone?https://github.com/sindresorhus/delay.git
          #?npm?i?-g?yarn
          cd?delay?&&?yarn?i
          #?VSCode?直接打開當(dāng)前項目
          #?code?.

          3. delay

          我們從零開始來實現(xiàn)一個比較完善的 delay 函數(shù)[3]

          3.1 第一版 簡版延遲

          要完成這樣一個延遲函數(shù)。

          3.1.1 使用

          (async()?=>?{
          ????await?delay1(1000);
          ????console.log('輸出這句');
          })();

          3.1.2 實現(xiàn)

          PromisesetTimeout 結(jié)合實現(xiàn),我們都很容易實現(xiàn)以下代碼。

          const?delay1?=?(ms)?=>?{
          ????return?new?Promise((resolve,?reject)?=>?{
          ????????setTimeout(()?=>?{
          ????????????resolve();
          ????????},?ms);
          ????});
          }

          我們要傳遞結(jié)果。

          3.2 第二版 傳遞 value 參數(shù)作為結(jié)果

          3.2.1 使用

          (async()?=>?{
          ????const?result?=?await?delay2(1000,?{?value:?'我是若川'?});
          ????console.log('輸出結(jié)果',?result);
          })();

          我們也很容易實現(xiàn)如下代碼。傳遞 value 最后作為結(jié)果返回。

          3.2.2 實現(xiàn)

          因此我們實現(xiàn)也很容易實現(xiàn)如下第二版。

          const?delay2?=?(ms,?{?value?}?=?{})?=>?{
          ????return?new?Promise((resolve,?reject)?=>?{
          ????????setTimeout(()?=>?{
          ????????????resolve(value);
          ????????},?ms);
          ????});
          }

          這樣寫,Promise 永遠(yuǎn)是成功。我們也需要失敗。這時我們定義個參數(shù) willResolve 來定義。

          3.3 第三版 willResolve 參數(shù)決定成功還是失敗。

          3.3.1 使用

          (async()?=>?{
          ????try{
          ????????const?result?=?await?delay3(1000,?{?value:?'我是若川',?willResolve:?false?});
          ????????console.log('永遠(yuǎn)不會輸出這句');
          ????}
          ????catch(err){
          ????????console.log('輸出結(jié)果',?err);
          ????}
          })();

          3.3.2 實現(xiàn)

          加個 willResolve 參數(shù)決定成功還是失敗。于是我們有了如下實現(xiàn)。

          const?delay3?=?(ms,?{value,?willResolve}?=?{})?=>?{
          ????return?new?Promise((resolve,?reject)?=>?{
          ????????setTimeout(()?=>?{
          ????????????if(willResolve){
          ????????????????resolve(value);
          ????????????}
          ????????????else{
          ????????????????reject(value);
          ????????????}
          ????????},?ms);
          ????});
          }

          3.4 第四版 一定時間范圍內(nèi)隨機(jī)獲得結(jié)果

          延時器的毫秒數(shù)是寫死的。我們希望能夠在一定時間范圍內(nèi)隨機(jī)獲取到結(jié)果。

          3.4.1 使用

          (async()?=>?{
          ????try{
          ????????const?result?=?await?delay4.reject(1000,?{?value:?'我是若川',?willResolve:?false?});
          ????????console.log('永遠(yuǎn)不會輸出這句');
          ????}
          ????catch(err){
          ????????console.log('輸出結(jié)果',?err);
          ????}

          ????const?result2?=?await?delay4.range(10,?20000,?{?value:?'我是若川,range'?});
          ????console.log('輸出結(jié)果',?result2);
          })();

          3.4.2 實現(xiàn)

          我們把成功 delay 和失敗 reject 封裝成一個函數(shù),隨機(jī) range 單獨封裝成一個函數(shù)。

          const?randomInteger?=?(minimum,?maximum)?=>?Math.floor((Math.random()?*?(maximum?-?minimum?+?1))?+?minimum);

          const?createDelay?=?({willResolve})?=>?(ms,?{value}?=?{})?=>?{
          ????return?new?Promise((relove,?reject)?=>?{
          ????????setTimeout(()?=>?{
          ????????????if(willResolve){
          ????????????????relove(value);
          ????????????}
          ????????????else{
          ????????????????reject(value);
          ????????????}
          ????????},?ms);
          ????});
          }

          const?createWithTimers?=?()?=>?{
          ????const?delay?=?createDelay({willResolve:?true});
          ????delay.reject?=?createDelay({willResolve:?false});
          ????delay.range?=?(minimum,?maximum,?options)?=>?delay(randomInteger(minimum,?maximum),?options);
          ????return?delay;
          }
          const?delay4?=?createWithTimers();

          實現(xiàn)到這里,相對比較完善了。但我們可能有需要提前結(jié)束。

          3.5 第五版 提前清除

          3.5.1 使用

          (async?()?=>?{
          ????const?delayedPromise?=?delay5(1000,?{value:?'我是若川'});

          ????setTimeout(()?=>?{
          ????????delayedPromise.clear();
          ????},?300);

          ????//?300?milliseconds?later
          ????console.log(await?delayedPromise);
          ????//=>?'我是若川'
          })();

          3.5.2 實現(xiàn)

          聲明 settle變量,封裝 settle 函數(shù),在調(diào)用 delayPromise.clear 時清除定時器。于是我們可以得到如下第五版的代碼。

          const?randomInteger?=?(minimum,?maximum)?=>?Math.floor((Math.random()?*?(maximum?-?minimum?+?1))?+?minimum);

          const?createDelay?=?({willResolve})?=>?(ms,?{value}?=?{})?=>?{
          ????let?timeoutId;
          ????let?settle;
          ????const?delayPromise?=?new?Promise((resolve,?reject)?=>?{
          ????????settle?=?()?=>?{
          ????????????if(willResolve){
          ????????????????resolve(value);
          ????????????}
          ????????????else{
          ????????????????reject(value);
          ????????????}
          ????????}
          ????????timeoutId?=?setTimeout(settle,?ms);
          ????});

          ????delayPromise.clear?=?()?=>?{
          ????????clearTimeout(timeoutId);
          ??timeoutId?=?null;
          ??settle();
          ????};

          ????return?delayPromise;
          }

          const?createWithTimers?=?()?=>?{
          ????const?delay?=?createDelay({willResolve:?true});
          ????delay.reject?=?createDelay({willResolve:?false});
          ????delay.range?=?(minimum,?maximum,?options)?=>?delay(randomInteger(minimum,?maximum),?options);
          ????return?delay;
          }
          const?delay5?=?createWithTimers();

          3.6 第六版 取消功能

          我們查閱資料可以知道有 AbortController 可以實現(xiàn)取消功能。

          caniuse AbortController[4]

          npm abort-controller[5]

          mdn AbortController[6]

          fetch-abort[7]

          fetch#aborting-requests[8]

          yet-another-abortcontroller-polyfill[9]

          3.6.1 使用

          (async?()?=>?{
          ????const?abortController?=?new?AbortController();

          ????setTimeout(()?=>?{
          ????????abortController.abort();
          ????},?500);

          ????try?{
          ????????await?delay6(1000,?{signal:?abortController.signal});
          ????}?catch?(error)?{
          ????????//?500?milliseconds?later
          ????????console.log(error.name)
          ????????//=>?'AbortError'
          ????}
          })();

          3.6.2 實現(xiàn)

          const?randomInteger?=?(minimum,?maximum)?=>?Math.floor((Math.random()?*?(maximum?-?minimum?+?1))?+?minimum);

          const?createAbortError?=?()?=>?{
          ?const?error?=?new?Error('Delay?aborted');
          ?error.name?=?'AbortError';
          ?return?error;
          };

          const?createDelay?=?({willResolve})?=>?(ms,?{value,?signal}?=?{})?=>?{
          ????if?(signal?&&?signal.aborted)?{
          ??return?Promise.reject(createAbortError());
          ?}

          ????let?timeoutId;
          ????let?settle;
          ????let?rejectFn;
          ????const?signalListener?=?()?=>?{
          ????????clearTimeout(timeoutId);
          ????????rejectFn(createAbortError());
          ????}
          ????const?cleanup?=?()?=>?{
          ??if?(signal)?{
          ???signal.removeEventListener('abort',?signalListener);
          ??}
          ?};
          ????const?delayPromise?=?new?Promise((resolve,?reject)?=>?{
          ????????settle?=?()?=>?{
          ???cleanup();
          ???if?(willResolve)?{
          ????resolve(value);
          ???}?else?{
          ????reject(value);
          ???}
          ??};

          ????????rejectFn?=?reject;
          ????????timeoutId?=?setTimeout(settle,?ms);
          ????});
          ????
          ????if?(signal)?{
          ??signal.addEventListener('abort',?signalListener,?{once:?true});
          ?}

          ????delayPromise.clear?=?()?=>?{
          ??clearTimeout(timeoutId);
          ??timeoutId?=?null;
          ??settle();
          ?};

          ????return?delayPromise;
          }

          const?createWithTimers?=?()?=>?{
          ????const?delay?=?createDelay({willResolve:?true});
          ????delay.reject?=?createDelay({willResolve:?false});
          ????delay.range?=?(minimum,?maximum,?options)?=>?delay(randomInteger(minimum,?maximum),?options);
          ????return?delay;
          }
          const?delay6?=?createWithTimers();

          3.7 第七版 自定義 clearTimeout 和 setTimeout 函數(shù)

          3.7.1 使用

          const?customDelay?=?delay7.createWithTimers({clearTimeout,?setTimeout});

          (async()?=>?{
          ????const?result?=?await?customDelay(100,?{value:?'我是若川'});

          ????//?Executed?after?100?milliseconds
          ????console.log(result);
          ????//=>?'我是若川'
          })();

          3.7.2 實現(xiàn)

          傳遞 clearTimeout, setTimeout 兩個參數(shù)替代上一版本的clearTimeout,setTimeout。于是有了第七版。這也就是delay的最終實現(xiàn)。

          ????const?randomInteger?=?(minimum,?maximum)?=>?Math.floor((Math.random()?*?(maximum?-?minimum?+?1))?+?minimum);

          const?createAbortError?=?()?=>?{
          ?const?error?=?new?Error('Delay?aborted');
          ?error.name?=?'AbortError';
          ?return?error;
          };

          const?createDelay?=?({clearTimeout:?defaultClear,?setTimeout:?set,?willResolve})?=>?(ms,?{value,?signal}?=?{})?=>?{
          ????if?(signal?&&?signal.aborted)?{
          ??return?Promise.reject(createAbortError());
          ?}

          ????let?timeoutId;
          ????let?settle;
          ????let?rejectFn;
          ????const?clear?=?defaultClear?||?clearTimeout;

          ????const?signalListener?=?()?=>?{
          ????????clear(timeoutId);
          ????????rejectFn(createAbortError());
          ????}
          ????const?cleanup?=?()?=>?{
          ??if?(signal)?{
          ???signal.removeEventListener('abort',?signalListener);
          ??}
          ?};
          ????const?delayPromise?=?new?Promise((resolve,?reject)?=>?{
          ????????settle?=?()?=>?{
          ???cleanup();
          ???if?(willResolve)?{
          ????resolve(value);
          ???}?else?{
          ????reject(value);
          ???}
          ??};

          ????????rejectFn?=?reject;
          ????????timeoutId?=?(set?||?setTimeout)(settle,?ms);
          ????});
          ????
          ????if?(signal)?{
          ??signal.addEventListener('abort',?signalListener,?{once:?true});
          ?}

          ????delayPromise.clear?=?()?=>?{
          ??clear(timeoutId);
          ??timeoutId?=?null;
          ??settle();
          ?};

          ????return?delayPromise;
          }

          const?createWithTimers?=?clearAndSet?=>?{
          ????const?delay?=?createDelay({...clearAndSet,?willResolve:?true});
          ????delay.reject?=?createDelay({...clearAndSet,?willResolve:?false});
          ????delay.range?=?(minimum,?maximum,?options)?=>?delay(randomInteger(minimum,?maximum),?options);
          ????return?delay;
          }
          const?delay7?=?createWithTimers();
          delay7.createWithTimers?=?createWithTimers;

          4. axios 取消請求

          axios取消原理是:通過傳遞 config 配置 cancelToken 的形式,來取消的。判斷有傳cancelToken,在 promise 鏈?zhǔn)秸{(diào)用的 dispatchRequest 拋出錯誤,在 adapterrequest.abort() 取消請求,使 promise 走向 rejected,被用戶捕獲取消信息。

          更多查看我的 axios 源碼文章取消模塊 學(xué)習(xí) axios 源碼整體架構(gòu),取消模塊(可點擊)

          5. 總結(jié)

          我們從零開始實現(xiàn)了一個帶取消功能比較完善的延遲函數(shù)。也就是 delay 70多行源碼[11]的實現(xiàn)。

          包含支持隨機(jī)時間結(jié)束、提前清除、取消、自定義 clearTimeout、setTimeout等功能。

          取消使用了 mdn AbortController[12] ,由于兼容性不太好,社區(qū)也有了相應(yīng)的 npm abort-controller[13] 實現(xiàn) polyfill

          yet-another-abortcontroller-polyfill[14]

          建議克隆項目啟動服務(wù)調(diào)試?yán)樱∠髸由羁獭?/p>

          #?推薦克隆我的項目,保證與文章同步
          git?clone?https://github.com/lxchuan12/delay-analysis.git
          cd?delay-analysis
          #?我寫的例子都在?examples?這個文件夾中,可以啟動服務(wù)本地查看調(diào)試
          npx?http-server?examples
          #?打開?http://localhost:8080


          參考資料

          [1]

          本文倉庫 https://github.com/lxchuan12/delay-analysis.git,求個star^_^: https://github.com/lxchuan12/delay-analysis.git

          [2]

          delay 主文件僅70多行: https://github.com/sindresorhus/delay/blob/main/index.js






          今日話題

          略。分享、收藏、點贊、在看我的文章就是對我最大的支持~
          瀏覽 56
          點贊
          評論
          收藏
          分享

          手機(jī)掃一掃分享

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

          手機(jī)掃一掃分享

          分享
          舉報
          <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片 |