技術總結——JS是怎麼運行的

技術總結——JS的執行順序

最近做了一些大廠的面試題目,才發現對JS的運行機制掌握的還是很淺薄,看了不少文檔、博客後在這裏做一個簡單的總結

基礎知識

我們都知道,爲了避免對DOM的操作產生衝突(JS誕生之初只是爲了給頁面賦予一些動態效果),JS只有一個線程。即便是現在有了webworker來實現多線程進行任務處理,但實際上,JS的多線程其實是單線程模擬出來的。
所以,爲了保證JS的執行效率,異步成爲了JS的一個核心的技術點。

以往的印象

按照我之前的理解,所謂JS的異步操作就是註冊一個回調函數,當觸發回調函數的事件滿足了,就掛起當前執行的任務,轉而執行回調函數。
事實證明我把JS想的太簡單了~

事實一,事件循環

  1. 首先,一門單線程語言從上向下執行
  2. 當遇到同步任務,讓同步任務進入主線程開始執行;當遇到異步任務,註冊異步任務的回調
  3. 當異步任務的觸發條件完成,回調函數進入事件隊列
  4. 當主線程中的同步任務完成,將事件隊列中的任務拉入主線程
  5. 進入步驟2,繼續~

上面就是JS的執行順序,用一個簡單的栗子說明一下

function foo(){
  console.log('異步回調執行');
}
console.log('第一個同步任務');
setTimeout(foo);
console.log('第二個同步任務');

上面代碼的執行順序是:

  • 顯示第一個同步任務
  • 然後執行setTimeout將foo註冊爲回調,因爲我們沒有寫延時,所以foo進入了事件隊列
  • 接下來顯示第二個同步任務,至此主線程空了
  • foo被拉入主線程開始執行,顯示異步回調執行

事實二,宏任務和微任務

除了同步任務和異步任務的差別,有時我們還需要進一步對任務進行劃分,也就是宏任務和微任務。

  • 宏任務主要包括script,setTimeout,setInterval
  • 微任務主要包括Promise,process.nextTick

隨着任務的細化,任務隊列也進一步分開了分爲宏任務隊列和微任務隊列。

這時的執行順序如下:

  1. 首先script作爲第一個宏任務進入主線程開始執行
  2. 自上而下執行,當遇到同步任務直接在主線程執行;當遇到異步宏任務,進入宏任務隊列;當遇到異步微任務,進入微任務隊列
  3. 當前宏任務(第一次是script)結束後,檢查微任務隊列是否爲空,如果不爲空就拉入主線程執行。
  4. 當微任務隊列執行完成後,從宏任務隊列拉取一個宏任務進入主線程執行,轉到步驟3

我們還是舉一個栗子

setTimeout(function() {
	new Promise(function(resolve) {
	    console.log('promise');
	}).then(function() {
	    console.log('then又是一個微任務');
	})
    console.log('setTimeout是一個宏任務');
})

new Promise(function(resolve) {
    console.log('promise');
}).then(function() {
    console.log('then是一個微任務');
})

console.log('console');

這段代碼的執行順序如下:

  • 首先整段代碼作爲一個宏任務,進入主線程執行
  • 遇到第一個setTimeout,將他的回調函數放到宏任務隊列
  • 遇到下面的promisethen,放到微任務隊列
  • 執行console.log,第一個宏任務結束,開始拉取微任務隊列,顯示then是一個微任務,微任務隊列空
  • 進入宏任務隊列執行setTimeout的回調函數,將新的promise.then放到微任務隊列,顯示setTimeout是一個宏任務,當前宏任務結束,拉取微任務隊列
  • 顯示then又是一個微任務,微任務隊列空
  • 宏任務和微任務全部執行完成,程序執行完成

我的理解大致就是這樣,如果有哪裏寫的不正確歡迎指正~

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章