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

大家好,我是若川。最近組織了源碼共讀活動,感興趣的可以點此加我微信 ruochuan12?參與,每周大家一起學習200行左右的源碼,共同進步。同時極力推薦訂閱我寫的《學習源碼整體架構系列》?包含20余篇源碼文章。

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

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

閱讀本文,你將學到:

1.?學會如何實現一個比較完善的?delay?函數
2.?學會使用?AbortController?實現取消功能
3.?學會面試常考?axios?取消功能實現
4.?等等

2. 環境準備

#?推薦克隆我的項目,保證與文章同步
git?clone?https://github.com/lxchuan12/delay-analysis.git
#?npm?i?-g?yarn
cd?delay-analysis/delay?&&?yarn?i
#?VSCode?直接打開當前項目
#?code?.
#?我寫的例子都在?examples?這個文件夾中,可以啟動服務本地查看調試
#?在?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?直接打開當前項目
#?code?.

3. delay

我們從零開始來實現一個比較完善的 delay 函數[3]

3.1 第一版 簡版延遲

要完成這樣一個延遲函數。

3.1.1 使用

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

3.1.2 實現

PromisesetTimeout 結合實現,我們都很容易實現以下代碼。

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

我們要傳遞結果。

3.2 第二版 傳遞 value 參數作為結果

3.2.1 使用

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

我們也很容易實現如下代碼。傳遞 value 最后作為結果返回。

3.2.2 實現

因此我們實現也很容易實現如下第二版。

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

這樣寫,Promise 永遠是成功。我們也需要失敗。這時我們定義個參數 willResolve 來定義。

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

3.3.1 使用

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

3.3.2 實現

加個 willResolve 參數決定成功還是失敗。于是我們有了如下實現。

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

3.4 第四版 一定時間范圍內隨機獲得結果

延時器的毫秒數是寫死的。我們希望能夠在一定時間范圍內隨機獲取到結果。

3.4.1 使用

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

3.4.2 實現

我們把成功 delay 和失敗 reject 封裝成一個函數,隨機 range 單獨封裝成一個函數。

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();

實現到這里,相對比較完善了。但我們可能有需要提前結束。

3.5 第五版 提前清除

3.5.1 使用

(async?()?=>?{const?delayedPromise?=?delay5(1000,?{value:?'我是若川'});setTimeout(()?=>?{delayedPromise.clear();},?300);//?300?milliseconds?laterconsole.log(await?delayedPromise);//=>?'我是若川'
})();

3.5.2 實現

聲明 settle變量,封裝 settle 函數,在調用 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 可以實現取消功能。

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?laterconsole.log(error.name)//=>?'AbortError'}
})();

3.6.2 實現

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

3.7.1 使用

const?customDelay?=?delay7.createWithTimers({clearTimeout,?setTimeout});(async()?=>?{const?result?=?await?customDelay(100,?{value:?'我是若川'});//?Executed?after?100?millisecondsconsole.log(result);//=>?'我是若川'
})();

3.7.2 實現

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

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 鏈式調用的 dispatchRequest 拋出錯誤,在 adapterrequest.abort() 取消請求,使 promise 走向 rejected,被用戶捕獲取消信息。

更多查看我的 axios 源碼文章取消模塊 學習 axios 源碼整體架構,取消模塊(可點擊)

5. 總結

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

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

取消使用了 mdn AbortController[12] ,由于兼容性不太好,社區也有了相應的 npm abort-controller[13] 實現 polyfill

yet-another-abortcontroller-polyfill[14]

建議克隆項目啟動服務調試例子,印象會更加深刻。

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

最后可以持續關注我@若川。歡迎加我微信 ruochuan12 交流,參與 源碼共讀 活動,每周大家一起學習200行左右的源碼,共同進步。

參考資料

[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



更多點擊閱讀原文查看。

a1a9a663ef5e46d353c32fbaaadb0dcc.gif

·················?若川簡介?·················

你好,我是若川,畢業于江西高校。現在是一名前端開發“工程師”。寫有《學習源碼整體架構系列》20余篇,在知乎、掘金收獲超百萬閱讀。
從2014年起,每年都會寫一篇年度總結,已經寫了7篇,點擊查看年度總結。
同時,最近組織了源碼共讀活動,幫助3000+前端人學會看源碼。公眾號愿景:幫助5年內前端人走向前列。

d7595a563eed89e6de347d675d91bc3d.png

識別方二維碼加我微信、拉你進源碼共讀

今日話題

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

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/news/275068.shtml
繁體地址,請注明出處:http://hk.pswp.cn/news/275068.shtml
英文地址,請注明出處:http://en.pswp.cn/news/275068.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

關于base64編碼的原理及實現

我們的圖片大部分都是可以轉換成base64編碼的data:image。 這個在將canvas保存為img的時候尤其有用。雖然除ie外,大部分現代瀏覽器都已經支持原生的基于base64的encode和decode,例如btoa和atob。(將canvas畫布保存成img并強制改變…

Django web開發系列(五)模板

一 前言在上一節了解到視圖函數處理后,會將結果渲染到創建的html頁面,但html如何接收并顯示視圖函數返回的動態數據呢?最常用的做法就是使用模板(Template),本節將簡單介紹一下模板的作用和用法。 可以這樣簡單的理解模板的概念&a…

facebook 面試_如何為您的Facebook產品設計面試做準備

facebook 面試重點 (Top highlight)Last month, I joined Facebook to work on Instagram DMs and as a way to pay it forward, I 上個月,我加入了Facebook,從事Instagram DM的工作,作為一種支付方式,我 offered to help anyone…

8年了,開始寫點東西了

大家好,我是若川。最近組織了源碼共讀活動,感興趣的可以點此加我微信 ruochuan12 參與,每周大家一起學習200行左右的源碼,共同進步。同時極力推薦訂閱我寫的《學習源碼整體架構系列》 包含20余篇源碼文章。今天分享一位大佬的文章…

荒徑 弗羅斯特_弗羅斯特龐克,顛覆性城市建設者

荒徑 弗羅斯特Most gamers are familiar with Will Wright’s famous SimCity series. It created the city building genre and there have been many attempts over the years to ape it. But few developers have been bold enough to completely deconstruct the formula; …

2012年1月份第2周51Aspx源碼發布詳情

WP7手指畫圖應用源碼 2012-01-14 [VS2010] 游戲介紹:Windows Phone 7手指畫圖應用 – FingerPaint,您通過此游戲可以隨心畫一些感興趣的東西,陶冶情操。操作簡單,頁面簡潔。適合新手學習參考。 WP7 Car Bloke(交通工具開銷記錄)源…

Gitee 如何自動部署博客 Pages?推薦用這個GitHub Actions!

大家好,我是若川。最近組織了源碼共讀活動,感興趣的可以點此加我微信 ruochuan12 參與,每周大家一起學習200行左右的源碼,共同進步。同時極力推薦訂閱我寫的《學習源碼整體架構系列》 包含20余篇源碼文章。前段時間我把自己的博客…

Java io流學習總結(三)

轉載于:https://www.cnblogs.com/ll409546297/p/7197911.html java.io幾種讀寫文件的方式 一、Java把這些不同來源和目標的數據都統一抽象為數據流。 Java語言的輸入輸出功能是十分強大而靈活的。 在Java類庫中,IO部分的內容是很龐大的,因為它…

現在流行的畫原型圖工具_原型資源圖:8種流行原型工具的綜合指南

現在流行的畫原型圖工具Although tools are not the most important things to learn as a UX designer, inevitably you need to use it in order to achieve your more important goals, to solve user’s problems. This article covers today’s 8 popular UX prototyping …

持續5個月,200+筆記,3千多人參與,邀請你來學源碼~

注意:本文點擊文末閱讀原文可查看文中所有鏈接。我正在參加掘金年度人氣作者投票活動,大家有空可以加微信群幫忙投票,感謝大家!想起今天還沒發文,就開放下微信群二維碼,大家掃碼進群讀源碼和幫忙投票吧。群…

自己動手開發調試器 01

背景: 在做XXX編譯器檢證時經常需要區分是代碼端錯誤,還是編譯器端錯誤,因此對代碼進行調試是必不可少的。但是狗日的甲方并沒有提供對應的調試器XXXDB,而用GDB調試XXX生成的可執行程序很不穩定,經常出現異常,干脆…

02如何抓住重點,系統高效地學習數據結構與算法?

以下內容總結自極客時間王爭大佬的《數據結構與算法之美》課程,本文章僅供個人學習總結。 什么是數據結構?什么是算法? 從廣義上講,數據結構就是指一組數據的存儲結構。算法就是操作數據的一組方法。 類比圖書館的書籍,我們如果想找一本書可…

第2年,倒數第3天,1.5萬票,感動!

1源碼共讀大家好,我是若川。眾所周知。從8月份開始,我組織了源碼共讀活動,至今已經有5個月了,每周一期,進行到了第18期。每周堅持寫源碼解讀文章,每天堅持答疑解惑,幫助了很多人學會看源碼&…

啟發式搜索給神經網絡_神經科學如何支持UX啟發式

啟發式搜索給神經網絡重點 (Top highlight)Interaction and UX designers have long known and used heuristics to guide the creation of a user-friendly interface. We know empirically that these principles work, and they make “common sense”. These heuristics th…

Django實戰(1):需求分析和設計

Depot是《Agile Web Development with Rails》中的一個購物車應用。 該書中用多次迭代的方法,逐步實現購物車應用,使很多人走上了rails開發的道路。 遺憾的是Django世界中好像沒有類似的指引,也許是因為pythoner 不需要具體的例子。 但是如果…

使用 apiDoc 為你的Node.js API 生成文檔

翻譯: 瘋狂的技術宅 原文:jonathas.com/documenting… 未經許可,禁止轉載! 當你為其他開發人員(前端,桌面,移動等)開發 API 時,需要生成一份風格良好的文檔,以…

海浪 shader_海浪下的發現

海浪 shaderI’ve been playing Subnautica for over 25 hours now, and likely have at least that many more to go. The game puts you in the shoes of a crew member on the Aurora, a spaceship that suffers a catastrophic incident and plummets to the largely ocean…

最后一天,特邀小姐姐配音拉票,今日可投28票

1源碼共讀大家好,我是若川。最后一天,特邀小姐姐配音拉票,超級好聽。眾所周知。從8月份開始,我組織了源碼共讀活動,至今已經有5個月了,每周一期,進行到了第18期。每周堅持寫源碼解讀文章&#x…

NET中使用Memcached的相關資源整理

本文轉自:http://www.cnblogs.com/dudu/archive/2009/07/19/1526407.html Memcached官方站點:http://www.danga.com/memcached / Memcached Win32 1.2.6下載:http://code.jellycan.com/memcached/ 安裝幫助:Windows下的.NET Memca…

FFMPEG 視頻圖像解封裝解碼

FFMPEG4.0 音頻解碼解封裝FFMPEG 音頻封裝編碼 下面的函數方法基于最新的FFMPEG 4.0(4.X):本文講是如何從一個視頻文件中提取出其中的圖像數據,并將圖像數據保存到文件中。 解碼解封裝的過程與音頻差不多,具體如下&…