淺談JS中的異步和單線程

爲什麼js引擎是單線程的?html

假如js引擎爲多線程,DOM操做可能很容易會出現混亂錯誤的狀況:好比某個時刻a線程要操做a節點時,線程b在時刻a以前已將a節點刪除了,這時便會出問題。ajax

異步能夠避免主線程阻塞,因此對於耗時/不肯定的操做,使用異步是很好的選擇。常見的有:處理ajax請求的線程、處理DOM事件的線程、定時器線程、讀寫文件的線程等。segmentfault

異步線程執行完畢後,會通知主線程執行相應的回調函數,這個通知機制的實現,以下:bash

消息(任務)隊列與事件循環(event loop)

  • 消息隊列:消息隊列是一個先進先出的隊列,它裏面存放着各類消息。
  • 事件循環:事件循環是指主線程重複從消息隊列中取消息、執行的過程。

實際上,主線程只會作一件事情,就是從消息隊列裏面取消息、執行消息,再取消息、再執行。當消息隊列爲空時,就會等待直到消息隊列變成非空。並且主線程只有在將當前的消息執行完成後,纔會去取下一個消息。這種機制就叫作事件循環機制,取一個消息並執行的過程叫作一次循環。數據結構

"任務隊列"中的事件,除了IO設備的事件之外,還包括一些用戶產生的事件(好比鼠標點擊、頁面滾動等等)。只要指定過回調函數,這些事件發生時就會進入"任務隊列",等待主線程讀取。多線程

"任務隊列"是一個先進先出的數據結構,排在前面的事件,優先被主線程讀取。主線程的讀取過程基本上是自動的,只要執行棧一清空,"任務隊列"上第一位的事件就自動進入主線程。可是,因爲存在"定時器"功能,主線程首先要檢查一下執行時間,某些事件只有到了規定的時間,才能返回主線程。異步

將邏輯用代碼表示:

while(true) {
    var message = queue.get();
    execute(message);
}
複製代碼

消息隊列中的消息具體是什麼呢?消息的具體結構是與具體的實現相關的,可是爲了簡單起見,咱們能夠認爲:函數

消息就是註冊異步任務時添加的回調函數。(多是不對的)
複製代碼

從生產者與消費者的角度看,異步過程是這樣的:oop

  • 異步線程是生產者,主線程是消費者(只有一個消費者)。異步線程執行異步任務,執行完成後把對應的回調函數封裝成一條消息放到消息隊列中;主線程不斷地從消息隊列中取消息並執行,當消息隊列空時主線程阻塞,直到消息隊列再次非空。

同步能夠保證順序一致,可是容易致使阻塞;異步能夠解決阻塞問題,可是會改變順序性。spa

參考文獻

JavaScript:完全理解同步、異步和事件循環(Event Loop)

JavaScript 運行機制詳解:再談Event Loop

相關文章
相關標籤/搜索