JavaScript 函數(shù)節(jié)流詳解及方法總結(jié)
JavaScript 函數(shù)節(jié)流詳解
瀏覽器一個(gè)網(wǎng)頁(yè)的UI線程只有一個(gè),他同時(shí)會(huì)處理界面的渲染和頁(yè)面JavaScript代碼的執(zhí)行(簡(jiǎn)單擴(kuò)展一下,瀏覽器或者JavaScript運(yùn)行大環(huán)境并不是單線程,諸如ajax異步回調(diào)、hybrid框架內(nèi)與native通信、事件隊(duì)列、CSS運(yùn)行線程等等都屬于多線程環(huán)境,不過ES6引入了Promise類來減少了部分異步情況)。因此當(dāng)JavaScript代碼運(yùn)行計(jì)算量很大的方法時(shí),就有可能阻塞UI線程,小則導(dǎo)致用戶響應(yīng)卡頓,嚴(yán)重的情況下瀏覽器會(huì)提示頁(yè)面無響應(yīng)是否強(qiáng)制關(guān)閉。例如網(wǎng)頁(yè)的頁(yè)面滾動(dòng)事件、移動(dòng)設(shè)備的滑動(dòng)、縮放事件等。即使沒有出現(xiàn)嚴(yán)重的性能問題,我們也應(yīng)該站在性能優(yōu)化的角度將短時(shí)間內(nèi)會(huì)多次觸發(fā)的大規(guī)模處理時(shí)間進(jìn)行分流計(jì)算。
如何有效避免UI線程運(yùn)行過長(zhǎng)的代碼,是所有用戶交互應(yīng)用需要考慮的問題,同樣的問題在客戶端Android可以使用UI主線程開子線程來分散計(jì)算。與此對(duì)應(yīng)的,js也可以通過引入webWorker來分散計(jì)算,但是在js中有一個(gè)更簡(jiǎn)單并且效果不錯(cuò)的方法:函數(shù)節(jié)流。使用函數(shù)節(jié)流的核心技巧就是使用定時(shí)器分段計(jì)算。具體的實(shí)現(xiàn)方式大致有兩種思路。
·方法一
1.這種實(shí)現(xiàn)方式的思路很好理解:設(shè)置一個(gè)一間隔時(shí)間,比如50毫秒,以此時(shí)間為基準(zhǔn)設(shè)置定時(shí)器,當(dāng)?shù)谝淮斡|發(fā)事件到第二次觸發(fā)事件間隔小于50毫秒時(shí),清除這個(gè)定時(shí)器,并設(shè)置一個(gè)新的定時(shí)器,以此類推,直到有一次事件觸發(fā)后50毫秒內(nèi)沒有重復(fù)觸發(fā)。代碼如下:
function debounce(method){ clearTimeout(method.timer); method.timer=setTimeout(function(){ method(); },50); }
這種設(shè)計(jì)方式有一個(gè)問題:本來應(yīng)該多次觸發(fā)的事件,可能最終只會(huì)發(fā)生一次。具體來說,一個(gè)循序漸進(jìn)的滾動(dòng)事件,如果用戶滾動(dòng)太快速,或者程序設(shè)置的函數(shù)節(jié)流間隔時(shí)間太長(zhǎng),那么最終滾動(dòng)事件會(huì)呈現(xiàn)為一個(gè)很突然的跳躍事件,中間過程都被節(jié)流截掉了。這個(gè)例子舉的有點(diǎn)夸張了,不過使用這種方式進(jìn)行節(jié)流最終是會(huì)明顯感受到程序比不節(jié)流的時(shí)候“更突?!?,這對(duì)于用戶體驗(yàn)是很差的。有一種彌補(bǔ)這種缺陷的設(shè)計(jì)思路。
·方法二
2.第二種實(shí)現(xiàn)方式的思路與第一種稍有差別:設(shè)置一個(gè)間隔時(shí)間,比如50毫秒,以此時(shí)間為基準(zhǔn)穩(wěn)定分隔事件觸發(fā)情況,也就是說100毫秒內(nèi)連續(xù)觸發(fā)多次事件,也只會(huì)按照50毫秒一次穩(wěn)定分隔執(zhí)行。代碼如下:
var oldTime=new Date().getTime(); var delay=50; function throttle1(method){ var curTime=new Date().getTime(); if(curTime-oldTime>=delay){ oldTime=curTime; method(); } }
相比于第一種方法,第二種方法也許會(huì)比第一種方法執(zhí)行更多次(有時(shí)候意味著更多次請(qǐng)求后臺(tái),即更多的流量),但是卻很好的解決了第一種方法清除中間過程的缺陷。因此在具體場(chǎng)景應(yīng)根據(jù)情況擇優(yōu)決定使用哪種方法。
對(duì)于方法二,我們?cè)偬峁┝硪环N同樣功能的寫法:
var timer=undefined,delay=50; function throttle2(method){ if(timer){ return ; } method(); timer=setTimeout(function(){ timer=undefined; },delay); }
最后說點(diǎn)個(gè)外話,說明一下函數(shù)節(jié)流的名稱問題,大家往往會(huì)看到throttle和debounce兩個(gè)方法名,throttle可以譯為“節(jié)制,卡住”,debounce可以譯為“防反跳”。在《JavaScript高級(jí)程序設(shè)計(jì)》中作者介紹了方法一,并且作者使用了“throttle”這個(gè)函數(shù)名。而在《第三方JavaScript編程》書中同時(shí)出現(xiàn)了方法一和方法二,作者將方法一命名為“debounce”,將方法二命名為“throttle”。國(guó)內(nèi)在同時(shí)介紹兩個(gè)方法的時(shí)候有些文章錯(cuò)誤的將方法一命名為“throttle”,而將方法二命名為“debounce”,從英語的角度來說是很不負(fù)責(zé)任的。因此在這里撥亂反正:方法一適合理解為“防反跳”,應(yīng)命名為“debounce”;方法二適合理解為“函數(shù)節(jié)制”,應(yīng)命名為“throttle”。
感謝閱讀,希望能幫助到大家,謝謝大家對(duì)本站的支持!
相關(guān)文章
利用JS響應(yīng)式修改vue實(shí)現(xiàn)頁(yè)面的input值
這篇文章主要給大家介紹了關(guān)于如何利用JS響應(yīng)式修改vue實(shí)現(xiàn)頁(yè)面的input值,文中通過示例代碼介紹的非常詳細(xì),對(duì)大家學(xué)習(xí)或者使用JS具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面來一起學(xué)習(xí)學(xué)習(xí)吧2019-09-096種JavaScript判斷對(duì)象自身為空的方法小結(jié)
這篇文章主要為大家詳細(xì)介紹了6種JavaScript判斷對(duì)象自身為空的方法,文中的示例代碼講解詳細(xì),感興趣的小伙伴可以跟隨小編一起學(xué)習(xí)一下2023-12-12很不錯(cuò)的兩款Bootstrap Icon圖標(biāo)選擇組件
這篇文章主要介紹了不容錯(cuò)過的兩款Bootstrap Icon圖標(biāo)選擇組件,支持自定義的圖標(biāo),拿出來分享下,絕對(duì)的干貨,感興趣的小伙伴們可以參考一下2016-01-01js實(shí)現(xiàn)圖片和鏈接文字同步切換特效的方法
這篇文章主要介紹了js實(shí)現(xiàn)圖片和鏈接文字同步切換特效的方法,涉及javascript操作文字及圖片的技巧,具有一定參考借鑒價(jià)值,需要的朋友可以參考下2015-02-02js相冊(cè)效果代碼(點(diǎn)擊創(chuàng)建即可)
利用js 書寫的相冊(cè)代碼,點(diǎn)擊創(chuàng)建即可看到效果,感興趣的朋友可以參考下哈,希望對(duì)你學(xué)習(xí)jquery有所幫助2013-04-04解決localstorage存儲(chǔ)boolean類型值的小坑
這篇文章主要介紹了解決localstorage存儲(chǔ)boolean類型值的小坑,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2021-06-06