解釋 JavaScript 非同步的運作原理

由於 JavaScript 是 Single Thread 語言,由於其 Single Thread 特色,對於繁重的運算動作,就無法如 C# 使用 Multi Thread 解決,因此 Asynchronous 在 JavaScript 格外重要。

Browser 使用獨特的 Event Loop Model 實現 Asynchronous,要能徹底了解其背後運作原理,才能掌握 JavaScript 的 Asynchronous。

Single Thread


  • One call stack
  • One thing at a time

JavaScript 最大的特色就是 Runtime 為 Single Thread,也就是只有一個 Call Stack,因此一個時間只能做一件事情。

也由於只有一個 Thread,只要遇到 大量運算,JavaScript 就會停住等待,因此使用者體驗就會不好。

C# 只要遇到 大量運算,我們就會使用 Multi Thread,但 JavaScript 為 Single Thread,因此這招不能用在 JavaScript

因此 Browser 做了擴充,有些東西並不是由 JavaScript Runtime 實作,而是由 Browser 提供:

  • DOM
  • AJAX (XMLHttpRequest)
  • setTimout()

也就是這三類 Browser 所提供的 API,屬於 Asynchronous 部分。

Event Loop Model


1
2
3
4
console.log('Hello ');
setTimeout(() => console.log('Sam'), 0);
console.log('World ');
// Hello World Sam

setTimeout()0 秒時,會先執行 console.log('Sam') 還是 console.log('World ') ?

既然 dealy 0 秒,且寫在前面,應該先執行吧 ?

1
2
3
4
concole.log('Hello ');
$.get('url', data => console.log(data)); // { name: 'Sam' }
console.log(' World');
// Hello World { name: 'Sam' }

當使用 AJAX 時,也會發現 console.log(' World') 會先執行。

eventloop000

會先執行 Stack 內的 function,直到 Stack 都執行完,才會執行 Event Loop,將 Callback Queue 內的 function 執行完。

1
console.log('Hello ');

loop001

  • console.log('Hello ') 進 Stack

loop002

  • 清空 Stack,在 Console 顯示 Hello
1
setTimeout(() => console.log('Sam'), 0);

loop003

  • setTimeout() 進 Stack

loop004

  • 清空 Stack,執行 Browser Thread 的 timer(0)

loop005

  • timer(0) 時間一到,將 callback 塞進 Callback Queue
1
console.log('World ');

loop006

  • console.log('World ') 進 Stack

loop007

  • 清空 Stack,在 Console 顯示 Hello World

loop008

  • 所有 Synchronous Function 都已經執行完,開始執行 Event Loop

loop009

  • 清空 Callback Queue,在 Console 顯示 Hello World Sam

Conclusion


  • Asynchronous Function 會將 Callback 先塞進 Callback Queue,不會立即執行
  • 等所有 Synchronous Function 都執行完,才開始執行 Event Loop 清空 Callback Queue 執行 Asynchronous Function

Reference


Philp Robers : What the heck is the event loop anyway ?

2018-10-07