Javascript事件循環入門到“忘記”(一).md

本文主要介紹Javascript事件循環在瀏覽器上的一些特性和應用介紹。

Javascript小知識

JavaScript的併發模型基於"事件循環"(Event Loop)。這個模型與像C或者Java這種其它語言中的模型截然不同。它永不阻塞,處理I/O通常通過事件和回調來執行,所以當一個應用正等待IndexedDB查詢返回或者一個XHR請求返回時,它仍然可以處理其它事情,如用戶輸入。【參:併發模型與事件循環

需要了解的幾點:

  • 單線程編程語言(Single Threaded)

    只有一個主線程(one thread),並且只有一個調用棧(Call Stack),因此同一時間只能執行同一件事情。【參:Philip Roberts: What the heck is the event loop anyway? | JSConf EU (4:15)】

  • 執行上下文(Execution Context)

    Javascript代碼執行時,會進入一個執行上下文。它可以理解爲當前代碼的運行環境(包括三種:全局環境、函數環境、Eval環境)。【參:Javascript核心技術開發解密 Page-11】

    糾正一點分享會可能存在的錯誤,它和作用域(Scope)不同!作用域是針對變量的一個可訪問區域,而執行上下文是屬於函數的指向的對象。(Scope pertains to the visibility of variables, and context refers to the object to which a function belongs.)【參:Why Should We Care About Scope and Context ?
  • 棧(stack)

    函數調用形成了一個棧幀。JavaScript中叫做調用棧(Call Stack);先進後出,後進先出(LIFO)。

    stack

  • 堆(heap)

    對象被分配在一個堆中,即用以表示一個大部分非結構化的內存區域。

  • 隊列(queue)

    一個JavaScript運行時包含了一個待處理的消息隊列。每一個消息都有一個爲了處理這個消息相關聯的函數。

  • 任務(Task)

    主要是隊列中要執行的函數。主要包含以下兩大類:

    1. macrotask:包含執行整體的js代碼,事件回調,XHR回調,定時器(setTimeout/setInterval/setImmediate),IO操作,UI render
    2. microtask:更新應用程序狀態的任務,包括promise回調,MutationObserver,process.nextTick,Object.observe

下圖展示了Event Loop的機制

event loop

這些代碼會發生什麼?

基於JQuery的Ajax示例,如果沒有異步多麼可怕!

// This is assuming that you're using jQuery
jQuery.ajax({
  url: 'https://api.example.com/endpoint',
  success: function(response) {
    // This is your callback.
  },
  async: false // And this is a terrible idea
});
// 原文網址:https://itw01.com/2Z6WE2L.html

這裏使用了JQuery的Ajax函數,併爲參數設置爲同步執行。那麼將遇到一種可怕的情況,這段代碼在success回調前,後面的Javascript代碼將不再執行。也就造成了可怕的阻塞(blocking)。

這段代碼什麼鬼,看着有點暈XD

let bar = 0
function foo() {
  bar++
  if (bar > 0) {
    return foo()
  }
}
foo()

沒錯,如果你不暈,說明你太棒了。這段代碼也會產生嚴重的問題。如下圖:

event loop

這是典型的內存溢出,可能會出現在某些場景下需要遞歸,但業務邏輯中的判斷又沒能正常計算進入到預設情況,於是調用棧中不斷進入foo(),又無法執行完,就造成內存溢出了。

糾正一處分享會中的錯誤,這個入棧過程沒有任何函數退出,所以會只進不出,導致內存爆炸。另外道哥提到的不斷累加到最大值爲負數的情況,我測試了一下JS下,會變成Infinite。某些其他語言(例如:C)是會變成-1,和二進制進位有關。

小測驗:Demo - 1

setTimeout(() => {
  console.log(1);
}, 0);
console.log(2);
for (let i = 0; i < 3; i++) {
  console.log(i);
}
console.log(4);

輸出結果:

A: 1, 2, 0, 1, 2, 4
B: 2, 4, 0, 1, 2, 1
C: 2, 0, 1, 2, 4, 1
D: 2, 4, 0, 1, 2, 1

小測驗:Demo - 2

console.log(1);
for (let i = 0; i < 3; i++) {
  setTimeout(() => {
    console.log('2-' + i);
  }, 0);
}
console.log(3);

輸出結果:

A: 1, 2-2, 2-2, 2-2, 3
B: 1, 3, 2-2, 2-2, 2-2
C: 1, 2-0, 2-1, 2-2, 3
D: 1, 3, 2-0, 2-1, 2-2

我想大家應該都正確答出來了吧:D,接下來我將詳細分析一些示例,以便於理解事件循環。

事件循環流程分析

示例分析1:

這裏我借用了作者稀土掘金深入理解事件迴圈和非同步流程控制文中的一段示範。

console.log('Hi')
setTimeout(function cb1() {
  console.log('cb1')
}, 5000)
console.log('Bye')

不論是否懂得事件循環的初學者,看到這段代碼應該也能猜出來答案是: Hi Bye cb1。畢竟cb1有一個5s的定時器。但是執行細節是怎樣的呢。我們來看下面這張gif圖。

一步步分析事件循環

圖中已經很清楚的展示了整個Javascript代碼是如何運作的。相信大家已經有較大的收穫了。

示例分析2:

我們來看這個頁面中的Javascript部分:

function one() {
  throw new Error('Oops!')
}
function two() {
  one()
}
function three() {
  two()
}
three()

我們在瀏覽器端執行時,打個斷點在throw new Error('Oops!')這一行。如下圖:

Demo - 6 圖1

在瞭解了事件循環的執行順序後,我們可以輕鬆知道他的執行順序,通過Chrome開發者工具、我們觀察圖中Call Stack區域,箭頭指向的one也正是我們斷點的地方,下面依次是two、three、(anonymous),這個是完全符合棧的先進後出,後進先出(last-in-first-out)的特徵~

我們在實際開發中,也可以通過Call Stack裏面觀察,找出上一層入口,分析異常原因。會有很大的幫助呢~

接着關閉斷點繼續執行,瀏覽器會拋出錯誤,錯誤信息如下,也是符合棧特點的

Demo - 6 圖2

其他

該文章中有部分內容在我製作的PPT中並未體現出來,對於這次分享會,我對Javascript一些運行機制有更深的理解,由於時間倉促也就在本次分享做了一點入門介紹。下期我將會結合更多的示例,對Node.js的事件循環與瀏覽器端的差異等等進行更深入的介紹,當大家都有所收穫後,就大可忘記了。

文中參考的一些資料(★表示推薦等級)

  1. 深入理解js事件循環機制(瀏覽器篇) ★★★★☆
  2. 深入理解事件迴圈和非同步流程控制 ★★★★☆
  3. Philip Roberts: What the heck is the event loop anyway? | JSConf EU ★★★★★

    這是一段來自Youtube的演講視頻,視頻中有用到一個工具"loupe - 模擬執行順序的工具",值得研究! ★★★★★
  4. Tasks, microtasks, queues and schedules ★★★★★

    文中有例子通過動畫來展示執行順序問題感覺超級棒!也對不通瀏覽器的結果有做分析,當然也許部分內容有些不一致,需要注意。
  5. [The JavaScript Event Loop [Presentation]](https://thomashunter.name/pos...

文中所提到的參考內容及使用到的PPT資料(有驚喜)

  1. JS事件循環PPT - Whidy
  2. JavaScript Event Loop - Thomas Hunter
  3. JS Event Loop - Sonle
  4. All you need to know about the JavaScript event loop - @sasatatar & @codaxy
  5. 全部整包下載
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章