Promise爲何比setTimeout先執行?

基本概念

爲便於理解,在進入正題以前,不得不說如下一些基本的概念。前端

進程與線程

進程(process)

  • 是操做系統結構的基礎;linux

  • 是系統進行資源分配和調度的基本單位;面試

  • 在linux系統中可使用ps-ef來查詢進程列表(以下圖)。比進程更小的單位叫作線程promise

    進程列表

線程(thread)

  • 是操做系統可以進行運算調度的最小單位;瀏覽器

  • 它被包含在進程之中,是進程中的實際運做單位;一個進程能夠併發多個線程,每條線程並行執行不一樣的任務。併發

併發(concurrency)
  • 一個處理器同時處理多個任務。
並行(parallelism)
  • 多個處理器或多核處理器同時處理多個任務。

單線程

  • 單線程在程序執行時,所走的程序路徑按照連續順序排下來,前面的必須處理好,後面的纔會執行。

同步與異步

同步(synchronous)

  • 調用一旦開始,調用者必須等到該調用結束才能執行下一步操做。

異步(asynchronous)

  • 方法一旦調用,就會當即返回,調用者即可進行下一步操做。

JS運行機制

執行棧

  • 是一個存儲函數調用的棧結構,遵循先進後出的原則。

主線程

  • 如今正在執行執行棧中的哪一個事件。

Event Loop

以上咱們講到了進程與線程同步與異步執行棧主線程等,那麼JS究竟是怎麼運行的呢?異步

先來供上一個常見面試題,寫出如下代碼的執行結果async

console.log('start')

    setTimeout(() => {
        console.log('setTimeout')
    }, 0)
    
    new Promise(resolve => {
        console.log('promise')
        resolve()
    }).then(() => {
        console.log('then1')
    }).then(() => {
        console.log('then2');
    })
    
    console.log('end')
複製代碼

結果以下圖:函數

運行結果

爲何setTimeout最後輸出呢???oop

JS代碼的執行其實就是往執行棧中放入函數。那麼遇到異步代碼的時候該怎麼辦呢?其實當遇到異步代碼時,會被掛起並在須要執行的時候加入到任務隊列。一旦執行棧爲空,Event Loop 就會從任務隊列中拿出須要執行的代碼並放入執行棧中執行。

JS引擎常駐於內存中,等待宿主將JS代碼或函數傳遞給它,也就是等待宿主環境分配宏觀任務,反覆等待 - 執行即爲事件循環。

Event Loop

Event Loop中,每一次循環稱爲tick,每一次tick的任務以下:

  • 執行棧選擇最早進入隊列的宏任務(通常都是script),執行其同步代碼直至結束;
  • 檢查是否存在微任務,有則會執行至微任務隊列爲空;
  • 若有必要會渲染頁面;
  • 開始下一輪tick,執行宏任務中的異步代碼(setTimeout的回調等)。

來一個超簡單的流程圖

宏任務與微任務

宏任務(macrotask)
  • 宿主(Node、瀏覽器)發起的任務;
  • 在ES6規範中,將其稱爲task;
  • script、setTimeout、setInterval、I/O、UI rendering、postMessage、MessageChannel、setImmediate
微任務(microtask)
  • JS引擎發起的任務;
  • 在ES6規範中,將其稱爲jobs;
  • Promise、MutaionObserver、process.nextTick

參考

掘金技術小冊(前端面試之道)第七小節

Js 的事件循環(Event Loop)機制以及實例講解

小結

本文主要介紹了JS運行機制,即Event Loop,及其相關的一系列概念。

若有問題,歡迎指正。

相關文章
相關標籤/搜索