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

          為什么要用 setTimeout 模擬 setInterval ?

          共 540字,需瀏覽 2分鐘

           ·

          2021-02-04 09:34


          來(lái)源:九旬
          https://segmentfault.com/a/1190000038829248


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

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

          下面我們一一羅列..

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

          定時(shí)器代碼:

          setInterval(fn(),?N);

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

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

          比如:

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

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

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

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

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

          //?做一個(gè)網(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è)定的間隔時(shí)差了:",
          ????????new?Date().getTime()?-?(startTime?+?count?*?1000),
          ????????"毫秒"
          ????);
          },?1000)
          輸出:
          //?與原設(shè)定的間隔時(shí)差了:567 毫秒
          //?與原設(shè)定的間隔時(shí)差了:552 毫秒
          //?與原設(shè)定的間隔時(shí)差了:563 毫秒
          //?與原設(shè)定的間隔時(shí)差了:554 毫秒(2次)
          //?與原設(shè)定的間隔時(shí)差了:564 毫秒
          //?與原設(shè)定的間隔時(shí)差了:602 毫秒
          //?與原設(shè)定的間隔時(shí)差了:573 毫秒
          //?與原設(shè)定的間隔時(shí)差了:633 毫秒

          setInterval 缺點(diǎn) 與 setTimeout 的不同

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

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

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

          綜上所述,setInterval 有兩個(gè)缺點(diǎn):

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

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

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

          來(lái)看一個(gè)經(jīng)典的例子來(lái)說(shuō)明他們的不同:

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

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

          為什么是一秒后輸出了 5 個(gè) 5 呢?簡(jiǎn)單來(lái)說(shuō),因?yàn)?for 是主線程代碼,先執(zhí)行完了,才輪到執(zhí)行 setTimeout

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

          setTimeout 模擬 setInterval

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

          • 在前一個(gè)定時(shí)器執(zhí)行完前,不會(huì)向隊(duì)列插入新的定時(shí)器(解決缺點(diǎn)一)
          • 保證定時(shí)器間隔(解決缺點(diǎn)二)

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

          1.寫一個(gè) 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.終止定時(shí)器

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

          參考

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


          最后


          • 歡迎加我微信(winty230),拉你進(jìn)技術(shù)群,長(zhǎng)期交流學(xué)習(xí)...

          • 歡迎關(guān)注「前端Q」,認(rèn)真學(xué)前端,做個(gè)專業(yè)的技術(shù)人...

          點(diǎn)個(gè)在看支持我吧
          瀏覽 64
          點(diǎn)贊
          評(píng)論
          收藏
          分享

          手機(jī)掃一掃分享

          分享
          舉報(bào)
          評(píng)論
          圖片
          表情
          推薦
          點(diǎn)贊
          評(píng)論
          收藏
          分享

          手機(jī)掃一掃分享

          分享
          舉報(bào)
          <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>
                  欧美操逼亚洲操逼 | 国产v亚洲v日韩v欧美v天堂V | 欧美人妖乱伦 | 99视频在线观看免费视频 | 天堂男人资源网 |