setTimeout 實現原理, 機制


setTimeout 實現原理, 機制

JS 執行機制說起

瀏覽器(或者說 JS 引擎)執行 JS 的機制是基於事件循環。

由於 JS 是單線程,所以同一時間只能執行一個任務,其他任務就得排隊,后續任務必須等到前一個任務結束才能開始執行。

為了避免因為某些長時間任務造成的無意義等待,JS 引入了異步的概念,用另一個線程來管理異步任務。

同步任務直接在主線程隊列中順序執行,而異步任務會進入另一個任務隊列,不會阻塞主線程;
等到主線程隊列空了(執行完了)的時候,就會去異步隊列查詢是否有可執行的異步任務了(異步任務通常進入異步隊列之后還要等一些條件才能執行,如 ajax 請求、文件讀寫),如果某個異步任務可以執行了便加入主線程隊列,以此循環;

定時器也是一種異步任務,通常瀏覽器都有一個獨立的定時器模塊,定時器的延遲時間就由定時器模塊來管理,當某個定時器到了可執行狀態,就會被加入主線程隊列。

setTimeout 注冊的函數 fn 會交給瀏覽器的定時器模塊來管理,延遲時間到了就將 fn 加入主進程執行隊列,如果隊列前面還有沒有執行完的代碼,則又需要花一點時間等待才能執行到 fn,所以實際的延遲時間會比設置的長;

如在 fn 之前正好有一個超級大循環,那延遲時間就不是一丁點了。



(function testSetTimeout() {
    const label = 'setTimeout';
    console.time(label);
    setTimeout(() => {
        console.timeEnd(label);
    }, 0);
    for(let i = 0; i < 1000; i++) {
        console.log(i);
    }
})();

// setTimeout: 133.2880859375ms


setInterval 的實現機制跟 setTimeout 類似,只不過 setInterval 是重復執行的。

對於 setInterval(fn, 100) 容易產生一個誤區:
並不是上一次 fn 執行完了之后再過 100ms 才開始執行下一次 fn。 事實上,setInterval 並不管上一次 fn 的執行結果,而是每隔 100ms 就將 fn 放入主線程隊列;
而兩次 fn 之間具體間隔多久就不一定了,跟 setTimeout 實際延遲時間類似,和 JS 執行情況有關。



(function testSetInterval() {
    let i = 0;
    const start = Date.now();
    const timer = setInterval(() => {
        i += 1;
        i === 5 && clearInterval(timer);
        console.log(`第${i}次開始`, Date.now() - start);
        for(let i = 0; i < 10000; i++) {}
        console.log(`第${i}次結束`, Date.now() - start);
    }, 100);
})();



  1. Stack 棧
  2. Queue 隊列
  3. Heap 堆

http://www.alloyteam.com/2016/05/javascript-timer/

requestAnimationFrame

window.requestAnimationFrame(callback);

https://developer.mozilla.org/en-US/docs/Web/API/window/requestAnimationFrame

// animation

var start = null;
var element = document.getElementById('SomeElementYouWantToAnimate');

function step(timestamp) {
  if (!start) start = timestamp;
  var progress = timestamp - start;
  element.style.transform = 'translateX(' + Math.min(progress / 10, 200) + 'px)';
  if (progress < 2000) {
    window.requestAnimationFrame(step);
  }
}

window.requestAnimationFrame(step);


https://caniuse.com/#feat=requestanimationframe

https://docs.microsoft.com/en-us/previous-versions/windows/internet-explorer/ie-developer/dev-guides/hh920765(v=vs.85)

https://css-tricks.com/using-requestanimationframe/


function repeatOften() {
  // Do whatever
  requestAnimationFrame(repeatOften);
}
requestAnimationFrame(repeatOften);


requestAnimationFrame demos

https://codepen.io/xgqfrms/pen/ZEzeaEL

https://codepen.io/xgqfrms/pen/zYOZPYd


(function testRequestAnimationFrame() {
    window.count = window.count || 0;
    window.count++;
   if(count < 100) {
    	const label = 'requestAnimationFrame';
        console.time(label);
        requestAnimationFrame(() => {
            console.timeEnd(label);
            testRequestAnimationFrame()
        });
   }
})();


setImmediate (IE / Edge)

do not use it!

https://developer.mozilla.org/en-US/docs/Web/API/Window/setImmediate

next.tick()

https://stackoverflow.com/questions/15349733/setimmediate-vs-nexttick

  1. 如果要將函數排在事件隊列中已有的任何I / O事件回調之后,請使用setImmediate;

  2. 使用process.nextTick有效地將函數排在事件隊列頭部,以便在當前函數完成后立即執行;


https://www.haorooms.com/post/js_setTimeout

setTimeout和setinterval的最主要區別是:

  1. setTimeou t只運行一次,也就是說設定的時間到后就觸發運行指定代碼,運行完后即結束;
    如果運行的代碼中再次運行同樣的setTimeout命令,則可循環運行。(即 要循環運行,需函數自身再次調用 setTimeout())

  2. setinterval是循環運行的,即每到設定時間間隔就觸發指定代碼; 這是真正的定時器。

setinterval使用簡單,而setTimeout則比較靈活,可以隨時退出循環,而且可以設置為按不固定的時間間隔來運行,比如第一次1秒,第二次2秒,第三次3秒。

事件循環模型

在單線程的 Javascript 引擎中,setTimeout() 是如何運行的呢,這里就要提到瀏覽器內核中的事件循環模型了;
簡單的講,在 Javascript 執行引擎之外,有一個任務隊列,當在代碼中調用 setTimeout() 方法時,注冊的延時方法會交由瀏覽器內核其他模塊(以 webkit 為例,是 webcore 模塊)處理,當延時方法到達觸發條件,即到達設置的延時時間時,這一延時方法被添加至任務隊列里;
這一過程由瀏覽器內核其他模塊處理,與執行引擎主線程獨立,執行引擎在主線程方法執行完畢,到達空閑狀態時,會從任務隊列中順序獲取任務來執行;
這一過程是一個不斷循環的過程,稱為事件循環模型。

Javascript 執行引擎的主線程運行的時候,產生堆(heap)和棧(stack);
程序中代碼依次進入棧中等待執行,當調用 setTimeout() 方法時,即圖中右側 WebAPIs 方法時,瀏覽器內核相應模塊開始延時方法的處理,
當延時方法到達觸發條件時,方法被添加到用於回調的任務隊列,只要執行引擎棧中的代碼執行完畢,主線程就會去讀取任務隊列,依次執行那些滿足觸發條件的回調函數。

http://www.alloyteam.com/2015/10/turning-to-javascript-series-from-settimeout-said-the-event-loop-model/

https://www.ruanyifeng.com/blog/2014/10/event-loop.html


Flag Counter

&copyxgqfrms 2012-2020

www.cnblogs.com 發布文章使用:只允許注冊用戶才可以訪問!



免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM