Javascript event loop

衆所周知, javascript是一個單線程語言。單線程也就意味着只有一個stack(調用棧),一次只能作一件事。那麼又是如何實現異步操做?先來了解幾個關鍵的術語。javascript

Call Stack 調用棧

clipboard.png
如圖所示,在運行過程當中,全部相關的變量是存於heap中,而call stack中是正在執行的代碼。經過F12 developer tool 在debug模式也能夠看到此時的call stack狀況。下圖是最簡單的一種狀況,函數根據調用順序依次進入call stack,執行後再依次彈出(stack後進先出)。java

clipboard.png

Task Queue

那麼JavaScript是怎麼實現異步的呢?重要的task queue(任務隊列)來了。
通常而言,咱們所理解的異步操做,都是放入task queue進行等待。以下代碼所示,console.log('start')進入call stack。而setTimeout是進入task queue進行等待。這裏設置的時間爲0,則是當即放入task queue,⚠️ 是放入task queue而不是當即執行。
只有在call stack爲空的時候,event loop會講task queue中的任務調入call stack再執行。promise

console.log('start');

setTimeout(()=>{
    console.log('hey')
}
, 0)

console.log('end');

輸出結果:瀏覽器

start
end
hey

clipboard.png

Macrotask

通常而言,macrotask queue就是咱們常說的task queue(也有人稱爲message queue)。Macrotask包括了setTimeout, Dom 操做(例如onLoad), click/mouse事件綁定,fetch response這類操做。實際上這些都是瀏覽器提供的API,因此在執行時是有它們單獨的線程去進行操做。舉個例子,setTimeout()設置了2s的延遲,是瀏覽器設置了timer來計時,是另外的線程在等待2秒,js主線程不受影響,2s後回調函數再進入task queue。異步

(function() {

  console.log('this is the start');

  setTimeout(function cb() {
    console.log('this is a msg from call back');
  });

  console.log('this is just a message');

  setTimeout(function cb1() {
    console.log('this is a msg from call back1');
  }, 0);

  console.log('this is the end');

})();

// "this is the start"
// "this is just a message"
// "this is the end"
// undefined (注意此時是函數返回,由於沒有設置返回值故輸出undefined) 
// "this is a msg from call back"
// "this is a msg from call back1"

Microtask

ES6提供了Promise來進行異步操做。爲了區別開task稱爲microtask。同上也有一個queue (job queue)來處理microtask。job queue擁有更高的優先級。每一個task結束後,都會進行perform a microtask checkpoint.也就是檢查job queue是否有microtask在等待執行,根據先進先出,依次執行。函數

console.log('script start');

setTimeout(function() {
  console.log('setTimeout');
}, 0);

Promise.resolve().then(function() {
  console.log('promise1');
}).then(function() {
  console.log('promise2');
});


// script start
// promise1
// promise2
// setTimeout

Event loop

簡單來講,event loop會檢查queue是否有須要處理的task,若是call stack爲空時,則會按照先進先出的順序來處理queue中的task。而task分爲microtask【Promise】和macrotask【setTimeout/DOM events/fetch】。優先處理microtask。一次event loop只會處理一次macrotask,而且是當microtask queue都處理結束後纔會去處理macrotasks。oop

while (queue.waitForMessage()) {
  queue.processNextMessage();
}

強烈推薦你們去看 https://jakearchibald.com/201... 做者用動畫的形式很是形象清晰地描述了過程。fetch

參考文章動畫

  1. The JavaScript Event Loop
  2. JavaScript Event Loop Explained
  3. EventLoop | MDN
  4. Tasks, microtasks, queues and schedules
相關文章
相關標籤/搜索