JavaScript在瀏覽器環境下的事件循環(Event Loop)

這裏主要記錄在平常中對知識的學習,經過結合筆記與自身理解的方式嘗試寫下總結
文章對細節可能不會一一介紹解釋,內容僅做參考
複製代碼

1、背景

  1. JavaScript是一門單線程語言
  2. 單線程所帶來的任務執行方式

2、同步任務與異步任務

  單線程就意味着,全部的任務都須要排隊,當前一個任務結束時,纔會執行後一個任務。若是前一個任務耗時很長,後一個任務就不得不一直等待。 若是是由於計算量大,CPU忙不過來倒也正常,可是不少時候CPU是閒着的,由於IO設備(輸入輸出設備)很慢(好比Ajax操做從網絡讀取數據),不得不等着結果出來,再往下執行。javascript

  這時主線程徹底能夠無論IO設備,掛起處於等待中的任務,先運行排在後面的任務。等到IO設備返回告終果,再回過頭,把掛起的任務繼續執行下去。因而,全部任務能夠分紅兩種:java

  1. 同步任務
  2. 異步任務

  在這裏用一張圖來講明:面試

  • 同步和異步任務分別進入不一樣的執行"場所",同步的進入主線程,異步的進入Event Table
  • 當異步任務完成時,Event Table會將對應的回調移入Event Queue中
  • JS引擎會持續不斷檢查主線程執行棧是否爲空,當主線程內的任務所有執行完畢後,會去Event Queue讀取排頭第一個,進入到主線程執行
  • 上述過程不斷重複,造成事件循環(Event Loop)

3、宏任務與微任務

  除了廣義的同步任務和異步任務,會對任務有更精細的定義:promise

  • 宏任務:總體代碼、setTimeout、setInterval
  • 微任務:Promise

  不一樣類型的任務會進入對應的Event Queue,好比setTimeout和setInterval會進入相同的Event Queue瀏覽器

  第一次進入總體代碼(宏任務)後,開始第一遍循環,在主線程任務所有執行完畢後,會先去讀取全部的微任務進行執行,而後再到宏任務,而宏任務裏面或許又包含着宏任務與微任務。以此不斷循環執行網絡

  用一張圖說明:異步

  舉個栗子函數

setTimeout(function() {
    console.log('setTimeout')
})

new Promise(function(resolve) {
    console.log('promise')
    resolve()
}).then(function() {
    console.log('then')
})

console.log('console')
複製代碼
  • 這段代碼會做爲宏任務,進入主線程
  • 先遇到setTimeout,將其放入Event Table,完成後會將其回調函數註冊並放入到宏任務Event Queue
  • 接下來遇到了Promise,new Promise當即執行,而後遇到console.log('promise'),當即執行。then函數放入微任務Event Queue。
  • 遇到console.log('console'),當即執行
  • 總體代碼做爲第一個宏任務執行結束,接着查看是否有微任務?咱們發現了then在微任務Event Queue裏面,執行
  • 微任務所有執行完畢,第一輪事件循環結束,開始第二輪循環。從宏任務Event Queue發現有setTimeout對應的回調函數(多個宏任務的時候取隊頭),當即執行
  • 檢查無可執行任務,結束

在理解時候須要將上面提到的兩點結合起來(同步異步 + 宏觀微觀),纔會造成機制。在代碼運行時,能夠想成是以代碼片斷來工做的oop


19.4.11 補充:今天在刷文章的時候看到相關的題目,感受能夠加深理解一波學習

console.log('sync1')
    
    setTimeout(function (){
        console.log('setTimeout')
    }, 0)
    
    var promise = new Promise(function(resolve, reject) {
        setTimeout(function() {
            console.log('setTimeout-Promise')
        }, 0)
        console.log('promise')
        resolve()
    })
    promise.then(() => {
        console.log('promise-Then')
        setTimeout(function() {
            console.log('promise-Timeout')
        }, 0)
    })
    
    setTimeout(function() {
        console.log('lastSetTimeout')
    }, 0)
    
    console.log('sync2')
複製代碼
  • 首先咱們看總體代碼,是第一遍同步執行,是第一個宏任務。按順序下來一共調用了三次setTimeout(Promise中的代碼也是同步執行的),調用了一次resolve,打印三次(按順序分別是sync一、promise、sync2)。因此它產生出三個宏任務、一個微任務。宏任務隊列按順序有setTimeout、setTimeout-Promise、lastSetTimeout / 微任務隊列按順序有promise-Then
  • 接下來,由於微任務隊列不爲空,先執行所有微任務。因此promise-Then被打印出來。而後又調用了一次setTimeout,因此promise-Timeout進入宏任務隊列。此時宏任務隊列按順序爲setTimeout、setTimeout-Promise、lastSetTimeout、promise-Timeout
  • 沒有微任務了,執行第二個宏任務,打印setTimeout,沒有再產生宏任務微任務,此時微任務隊列爲空,因此接着執行第三個宏任務
  • 同上,打印setTimeout-Promise
  • 同上,打印lastSetTimeout
  • 同上,打印promise-Timeout
  • 結果爲 sync一、promise、sync二、promise-Then、setTimeout、setTimeout-Promise、lastSetTimeout、promise-Timeout

  • 直到ES6,js才真正內建有直接的異步概念(Promise的引入)
  • 在promise出現以前,js並無異步,有異步的是寄主環境(文中指瀏覽器)
  • 微任務是js引擎內部的一種機制,而宏任務是寄主環境的一種機制
  • setTimeout在現設計中會有4ms的最小時間取值,就是最快也是4ms後才執行

面試相關:

  • 給題目,問結果輸出
  • 運行機制的原理 - 描述基礎
  • 運行機制的理解 - 實際運用
相關文章
相關標籤/搜索