js的Event Loop機制解析

廣工小成發表於2018-04-20

概述

最近看了一些非同步的文章,有一些作者沒有寫程式碼也把錯誤的理解放上來。想想,我也應該總結一些,之前面試也有過一道題目,雖然說是考察非同步,但其實就是考察非同步當中的任務佇列。給你一道題目,你覺得會依次輸出什麼?

console.log('1')

Promise.resolve().then(() => console.log('2'))

setTimeout(() => {console.log('3'); Promise.resolve().then(() => console.log('4'))}, 0)

Promise.resolve().then(() => console.log('5'))

setTimeout(() => console.log('6'), 0)

console.log('7')

答案是

1 7 2 5 3 4 6

如果你知道為什麼會輸出這些的話,那我想你不必看下面了,因為你也有大概的理解,如果沒有的話,我就跟你分析一下吧。

先看一張圖吧,是拿別人的,有部分原因也是因為他寫的文章有錯誤,我才總結。

image

先理解這張圖片吧,我簡單介紹一下。

  • 棧:主執行緒的函式執行,非同步操作的執行放在了非同步處理模組。
  • 堆:用來儲存引用型別的指向。
  • 非同步處理模組:主執行緒裡面的非同步模組。
  • 任務佇列:儲存非同步執行緒的執行佇列。

然後,js執行就是執行主執行緒->執行任務佇列

當然,這只是大概的介紹,真正的堆和棧並不是和他說的一樣,棧裡面還有記憶體棧和呼叫棧,記憶體棧又有全域性的記憶體棧,也有某個函式的記憶體棧,當然,函式內部的記憶體棧又放在了堆裡面。這裡面的棧,僅僅是代表了呼叫棧。

巨集任務佇列(macrotasks)

什麼是巨集任務佇列?

巨集任務佇列macrotasks: setTimeout, setInterval, setImmediate, I/O, UI rendering

上面的基本操作就是巨集任務佇列

微任務佇列(microtasks)

微任務佇列microtasks: process.nextTick, Promise, MutationObserver

上面的基本操作就是微任務佇列

Event Loop

我就簡單的說一下js裡面執行順序吧:

  1. 執行主執行緒,如果有非同步操作,則放到非同步佇列執行。執行2
  2. 當主執行緒執行完畢,判斷非同步佇列是否有微任務,如果有,則新增進去主執行緒執行;如果沒有則將最新可以執行的巨集任務加進主執行緒。返回1

是不是很簡單?
那麼上面那道題的結果無非就是

// 一開始,主執行緒
console.log('1');
console.log('7');

// 下一步,主執行緒
Promise.resolve().then(() => console.log('2'))

// 下一步,主執行緒
Promise.resolve().then(() => console.log('5'))

// 下一步,主執行緒
setTimeout(() => {console.log('3')}, 0)

// 下一步,主執行緒
Promise.resolve().then(() => console.log('4'))

// 下一步,主執行緒
setTimeout(() => console.log('6'), 0)

總結

看了一下很多文章,以為很難,很想畫圖,但是在寫的過程中,發現其實真的很簡單,只要好好了解js裡面的引擎就好了,js還是一個很強大的單執行緒語言。

相關文章