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

          【JS】986- 為什么要用 setTimeout 模擬 setInterval ?

          共 4431字,需瀏覽 9分鐘

           ·

          2021-06-13 13:14

          來源:九旬
          https://segmentfault.com/a/1190000038829248


          在JS 事件循環(huán)之宏任務(wù)和微任務(wù)中講到過,setInterval 是一個宏任務(wù)。

          用多了你就會發(fā)現(xiàn)它并不是準(zhǔn)確無誤,極端情況下還會出現(xiàn)一些令人費(fèi)解的問題。

          下面我們一一羅列..

          推入任務(wù)隊列后的時間不準(zhǔn)確

          定時器代碼:

          setInterval(fn(), N);

          上面這句代碼的意思其實是fn()將會在 N 秒之后被推入任務(wù)隊列。

          所以,在 setInterval 被推入任務(wù)隊列時,如果在它前面有很多任務(wù)或者某個任務(wù)等待時間較長比如網(wǎng)絡(luò)請求等,那么這個定時器的執(zhí)行時間和我們預(yù)定它執(zhí)行的時間可能并不一致。

          比如:

          let startTime = new Date().getTime();
          let count = 0;
          //耗時任務(wù)
          setInterval(function({
            let i = 0;
            while (i++ < 1000000000);
          }, 0);
          setInterval(function({
            count++;
            console.log(
              "與原設(shè)定的間隔時差了:",
              new Date().getTime() - (startTime + count * 1000),
              "毫秒"
            );
          }, 1000);
          // 輸出:
          // 與原設(shè)定的間隔時差了:699 毫秒
          // 與原設(shè)定的間隔時差了:771 毫秒
          // 與原設(shè)定的間隔時差了:887 毫秒
          // 與原設(shè)定的間隔時差了:981 毫秒
          // 與原設(shè)定的間隔時差了:1142 毫秒
          // 與原設(shè)定的間隔時差了:1822 毫秒
          // 與原設(shè)定的間隔時差了:1891 毫秒
          // 與原設(shè)定的間隔時差了:2001 毫秒
          // 與原設(shè)定的間隔時差了:2748 毫秒
          // ...

          可以看出來,相差的時間是越來越大的,越來越不準(zhǔn)確。

          函數(shù)操作耗時過長導(dǎo)致的不準(zhǔn)確

          考慮極端情況,假如定時器里面的代碼需要進(jìn)行大量的計算(耗費(fèi)時間較長),或者是 DOM 操作。這樣一來,花的時間就比較長,有可能前一次代碼還沒有執(zhí)行完,后一次代碼就被添加到隊列了。也會到時定時器變得不準(zhǔn)確,甚至出現(xiàn)同一時間執(zhí)行兩次的情況。

          最常見的出現(xiàn)的就是,當(dāng)我們需要使用 ajax 輪詢服務(wù)器是否有新數(shù)據(jù)時,必定會有一些人會使用 setInterval ,然而無論網(wǎng)絡(luò)狀況如何,它都會去一遍又一遍的發(fā)送請求,最后的間隔時間可能和原定的時間有很大的出入。

          // 做一個網(wǎng)絡(luò)輪詢,每一秒查詢一次數(shù)據(jù)。
          let startTime = new Date().getTime();
          let count = 0;

          setInterval(() => {
              let i = 0;
              while (i++ < 10000000); // 假設(shè)的網(wǎng)絡(luò)延遲
              count++;
              console.log(
                  "與原設(shè)定的間隔時差了:",
                  new Date().getTime() - (startTime + count * 1000),
                  "毫秒"
              );
          }, 1000)
          輸出:
          // 與原設(shè)定的間隔時差了:567 毫秒
          // 與原設(shè)定的間隔時差了:552 毫秒
          // 與原設(shè)定的間隔時差了:563 毫秒
          // 與原設(shè)定的間隔時差了:554 毫秒(2次)
          // 與原設(shè)定的間隔時差了:564 毫秒
          // 與原設(shè)定的間隔時差了:602 毫秒
          // 與原設(shè)定的間隔時差了:573 毫秒
          // 與原設(shè)定的間隔時差了:633 毫秒

          setInterval 缺點 與 setTimeout 的不同

          再次強(qiáng)調(diào),定時器指定的時間間隔,表示的是何時將定時器的代碼添加到消息隊列,而不是何時執(zhí)行代碼。所以真正何時執(zhí)行代碼的時間是不能保證的,取決于何時被主線程的事件循環(huán)取到,并執(zhí)行。

          setInterval(functionN)
          //即:每隔N秒把function事件推到消息隊列中
          setinterval-1.png

          上圖可見,setInterval 每隔 100ms 往隊列中添加一個事件;100ms 后,添加 T1 定時器代碼至隊列中,主線程中還有任務(wù)在執(zhí)行,所以等待,some event 執(zhí)行結(jié)束后執(zhí)行 T1 定時器代碼;又過了 100msT2 定時器被添加到隊列中,主線程還在執(zhí)行 T1 代碼,所以等待;又過了 100ms ,理論上又要往隊列里推一個定時器代碼,但由于此時 T2 還在隊列中,所以T3 不會被添加(T3 被跳過),結(jié)果就是此時被跳過;這里我們可以看到,T1 定時器執(zhí)行結(jié)束后馬上執(zhí)行了 T2 代碼,所以并沒有達(dá)到定時器的效果。

          綜上所述,setInterval 有兩個缺點:

          • 使用 setInterval 時,某些間隔會被跳過;
          • 可能多個定時器會連續(xù)執(zhí)行;

          可以這么理解:每個 setTimeout 產(chǎn)生的任務(wù)會直接 push 到任務(wù)隊列中;而 setInterval 在每次把任務(wù) push 到任務(wù)隊列前,都要進(jìn)行一下判斷(看上次的任務(wù)是否仍在隊列中,如果有則不添加,沒有則添加)。

          因而我們一般用 setTimeout 模擬 setInterval ,來規(guī)避掉上面的缺點。

          來看一個經(jīng)典的例子來說明他們的不同:

          for (var i = 0; i < 5; i++) {
            setTimeout(function({
              console.log(i);
            }, 1000);
          }

          做過的朋友都知道:是一次輸出了 55 ; 那么問題來了:是每隔 1 秒輸出一個 5 ?還是一秒后立即輸出 55 ?答案是:一秒后立即輸出 55因為 for 循環(huán)了五次,所以 setTimeout5 次添加到時間循環(huán)中,等待一秒后全部執(zhí)行。

          為什么是一秒后輸出了 55 呢?簡單來說,因為 for 是主線程代碼,先執(zhí)行完了,才輪到執(zhí)行 setTimeout 。

          當(dāng)然為什么輸出不是 15 ,這個涉及到作用域的問題了,這里就不解釋了。

          setTimeout 模擬 setInterval

          綜上所述,在某些情況下,setInterval 缺點是很明顯的,為了解決這些弊端,可以使用 setTimeout() 代替。

          • 在前一個定時器執(zhí)行完前,不會向隊列插入新的定時器(解決缺點一)
          • 保證定時器間隔(解決缺點二)

          具體實現(xiàn)如下:

          1.寫一個 interval 方法

          let timer = null
          interval(func, wait){
              let interv = function(){
                  func.call(null);
                  timer=setTimeout(interv, wait);
              };
              timer= setTimeout(interv, wait);
           },

          2.和 setInterval() 一樣使用它

          interval(function({}, 20);

          3.終止定時器

          if (timer) {
            window.clearSetTimeout(timer);
            timer = null;
          }

          參考

          • 為什么要用 setTimeout 模擬 setInterval ?
          • 用 settTimeout()代替 setInterval()
          聲明:文章著作權(quán)歸作者所有,如有侵權(quán),請聯(lián)系小編刪除。

          1. JavaScript 重溫系列(22篇全)
          2. ECMAScript 重溫系列(10篇全)
          3. JavaScript設(shè)計模式 重溫系列(9篇全)
          4. 正則 / 框架 / 算法等 重溫系列(16篇全)
          5. Webpack4 入門(上)|| Webpack4 入門(下)
          6. MobX 入門(上) ||  MobX 入門(下)
          7. 120+篇原創(chuàng)系列匯總

          回復(fù)“加群”與大佬們一起交流學(xué)習(xí)~

          點擊“閱讀原文”查看 120+ 篇原創(chuàng)文章

          瀏覽 61
          點贊
          評論
          收藏
          分享

          手機(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>
                  激情久久国产视频 | 天天插插综合视频综合 | 国产蜜臀在线观看 | 亚洲色大成人网站www永久 | 新超碰在线观看 |