setTimeout 與函數(shù)節(jié)流與防抖

setTimeout簡介

Javascript是單線程語言,不過允許通過設(shè)置超時(shí)調(diào)用(setTimeout)和間歇調(diào)用(setInterval)來調(diào)度代碼在特定時(shí)刻執(zhí)行。這兩個(gè)方法都屬于window對(duì)象,接受兩個(gè)參數(shù):第一個(gè)參數(shù)是要執(zhí)行的代碼(一般為函數(shù)),第二個(gè)是執(zhí)行代碼前需要等待的時(shí)間(ms)。

setTimeout :第二個(gè)參數(shù),即表示需要等待的時(shí)間,但經(jīng)過該時(shí)間后指定的代碼不一定會(huì)立即執(zhí)行。因?yàn)镴S是單線程的,為了控制要執(zhí)行的代碼,就有一個(gè)JS任務(wù)隊(duì)列,這些任務(wù)會(huì)按照它們被添加到隊(duì)列中的順序去執(zhí)行。而setTimeout的第二個(gè)參數(shù)就是指定經(jīng)過多長時(shí)間將第一個(gè)參數(shù)指定的任務(wù)添加到隊(duì)列中。如果任務(wù)隊(duì)列是空的,那么新添加的任務(wù)會(huì)立即執(zhí)行;否則它就要等前面的任務(wù)執(zhí)行完了以后才能開始執(zhí)行。

  // 休眠函數(shù)
   let sleep=function(n){
       let start=new Date().getTime();
       while(true){
           if(new Date().getTime()-start>n){
               break;
           }
       }
       console.log('wake up.')
   }

   // 超時(shí)調(diào)用
    var globalAttribute='global';
    let tmId=setTimeout(()=>console.log(this.globalAttribute),2500);
    for(let i=0;i<5;i++){
        setTimeout(()=>console.log(i),1000);
        sleep(1000);
    }

chrome中結(jié)果

注意for語句中使用了let聲明i(ES6語法),不同于通過var聲明。所以i的值不是for語句結(jié)束時(shí)的值。
從以上例子可以看出,兩個(gè)setTimeout都沒有按設(shè)定的時(shí)間執(zhí)行,因?yàn)槿蝿?wù)隊(duì)列中有for循環(huán)這個(gè)任務(wù)(里面的sleep函數(shù)比較耗時(shí))仍在執(zhí)行,只有等它執(zhí)行完畢,才會(huì)執(zhí)行超時(shí)調(diào)用中設(shè)置的任務(wù)。
setInterval :與setTimeout 類似,只是會(huì)每隔一段時(shí)間就會(huì)執(zhí)行,在不受干涉的情況下會(huì)一直執(zhí)行,所以要使用clearInterval取消。

介紹完setTimeout ,下面才是重頭戲。
函數(shù)節(jié)流:高頻事件觸發(fā),但在n秒內(nèi)只會(huì)執(zhí)行一次,所以節(jié)流會(huì)稀釋函數(shù)的執(zhí)行頻率。(例如一直拖動(dòng)進(jìn)度條,如果與scroll綁定的代碼不加控制,那只要拖動(dòng),相應(yīng)代碼就會(huì)高頻執(zhí)行;使用節(jié)流后,可以設(shè)置為特定時(shí)間(如500ms)才執(zhí)行一次scroll事件處理代碼)。
函數(shù)防抖:頻繁觸發(fā)的情況下,只有停下來一段事件,才執(zhí)行一次相應(yīng)代碼。(搜索引擎的聯(lián)想查詢功能,它是在用戶進(jìn)行鍵入的時(shí)候監(jiān)聽keypress事件,然后異步去查詢結(jié)果。如果用戶快速的輸入了一連串的字符,假設(shè)是10個(gè)字符,那么就會(huì)在短時(shí)間內(nèi)觸發(fā)了10次的請(qǐng)求,這無疑不是我們想要的。我們想要的是用戶停止輸入過了一會(huì)(很短,可以設(shè)定),才去觸發(fā)查詢的請(qǐng)求 )
區(qū)別:防抖和節(jié)流本質(zhì)是不一樣的。防抖是將多次執(zhí)行變?yōu)樽詈笠淮螆?zhí)行,節(jié)流是將多次執(zhí)行變成每隔一段時(shí)間執(zhí)行。

    function throttle(fn) {
        let canRun = true; // 通過閉包保存一個(gè)標(biāo)記
        return function () {
            if (!canRun) return; // 在函數(shù)開頭判斷標(biāo)記是否為true,不為true則return
            canRun = false; // 立即設(shè)置為false
            setTimeout(() => { // 將外部傳入的函數(shù)的執(zhí)行放在setTimeout中
                fn.apply(this, arguments);
                // 最后在setTimeout執(zhí)行完畢后再把標(biāo)記設(shè)置為true(關(guān)鍵)表示可以執(zhí)行下一次循環(huán)了。當(dāng)定時(shí)器沒有執(zhí)行的時(shí)候標(biāo)記永遠(yuǎn)是false,在開頭被return掉
                canRun = true;
            }, 500);
        };
    }
    function sayHi(e) {
        console.log(e.target.innerWidth, e.target.innerHeight);
    }
    window.addEventListener('resize', throttle(sayHi));

函數(shù)節(jié)流示意代碼

函數(shù)防抖
   function debounce(fn) {
      let timeout = null; // 創(chuàng)建一個(gè)標(biāo)記用來存放定時(shí)器的返回值
      return function () {
        clearTimeout(timeout); // 每當(dāng)用戶輸入的時(shí)候把前一個(gè) setTimeout clear 掉
        timeout = setTimeout(() => { // 然后又創(chuàng)建一個(gè)新的 setTimeout, 這樣就能保證輸入字符后的 interval 間隔內(nèi)如果還有字符輸入的話,就不會(huì)執(zhí)行 fn 函數(shù)
          fn.apply(this, arguments);
        }, 500);
      };
    }
    function sayThis() {
      console.log(this);
    }

    var inputEle = document.getElementById('inp');// <input type="text" id="input">
    inputEle.addEventListener('input', debounce(sayThis)); // <input type="text" id="input">

Note:這里面關(guān)于this其實(shí)有些細(xì)節(jié),首先要知道:(1)箭頭函數(shù)自己沒有this,如果箭頭函數(shù)被非箭頭函數(shù)包含,this 綁定的就是最近一層非箭頭函數(shù)的 this。(2)使用ele.addEventListener( 'specificEvent', eventHandler)給 ele 設(shè)置事件監(jiān)聽時(shí),傳入 eventHandlerthis指的就是 ele.
那么inputEle.addEventListener('input', debounce(sayThis)),給輸入元素上的input監(jiān)聽時(shí),設(shè)置的處理函數(shù)為 debounce(sayThis) 執(zhí)行返回的匿名函數(shù),該函數(shù)中的 this 指向的就是 輸入元素,進(jìn)而可以確定該函數(shù)中的箭頭函數(shù)內(nèi)的 this。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

友情鏈接更多精彩內(nèi)容