本文作為EVENLOOP事件循環的延伸:
執行順序:
------------循環----------
| |
=====微任務==》{宏任務==》微任務==》瀏覽器渲染}=====>>>>
^
||
======監控線程===等待=====有返回進行返回=====>>>>>
宏任務和微任務都是任務隊列,只是用來存放東西的隊列。
主線程可以執行代碼,渲染UI之類的。
主線程空閑的時候就會去輪詢看看宏任務隊列有沒有要執行的任務,沒有就過,有就取出來執行。
宏任務執行完會去看微任務隊列。沒有就過,有就取出來執行。
微任務都是在宏任務執行完,空閑的時候執行。
也可以用下面的圖表示
更細化的順序是: 同步--->微任務 ----> 異步
1. 什么是同步,什么是異步;
同步任務: 在主線程上排隊執行的任務,只有前一個任務執行完畢,才能執行下一個任務;
異步任務:不進入主線程,認識進入“任務隊列” 的任務,只有“任務隊列”通知主線程,某個異步任務可以執行了,該任務才會進入主線程執行
2. 什么是宏任務,什么微任務;
查了一下,貌似除了微任務,別的都是宏任務!
宏任務: 整體的 script 和 setTimeout 等!
而微任務貌似只有 : promise.then() (前端而言) node 好像還有一個---process.nextTick()
先來看一段代碼,很多面試題有這個:
console.log('script start'); setTimeout(function() { console.log('setTimeout'); }, 0); Promise.resolve().then(function() { console.log('promise1'); }).then(function() { console.log('promise2'); }); console.log('script end');
crome結果是:script start, script end, promise1, promise2, setTimeout,但是各瀏覽器不一致。
Microsoft Edge, Firefox 40, iOS Safari 及桌面 Safari 8.0.8 在 promise1 和 promise2 之前打印 setTimeout ——盡管這似乎是競爭條件導致的。奇怪的是,Firefox 39 和 Safari 8.0.7 是對的。
要知道為啥是這個順序,需要了解:事件循環的,tasks宏任務 和 microtasks微任務
宏任務
(macro)task,可以理解是每次執行棧執行的代碼就是一個宏任務(包括每次從事件隊列中獲取一個事件回調並放到執行棧中執行)。
瀏覽器為了能夠使得JS內部(macro)task與DOM任務能夠有序的執行,會在一個(macro)task執行結束后,在下一個(macro)task 執行開始前,對頁面進行重新渲染,流程如下:
(macro)task->渲染->(macro)task->...
宏任務包含:
script(整體代碼) setTimeout
setInterval
I/O UI交互事件
postMessage
MessageChannel
setImmediate(Node.js 環境)
微任務
microtask,可以理解是在當前 task 執行結束后立即執行的任務。也就是說,在當前task任務后,下一個task之前,在渲染之前。
所以它的響應速度相比setTimeout(setTimeout是task)會更快,因為無需等渲染。也就是說,在某一個macrotask執行完后,就會將在它執行期間產生的所有microtask都執行完畢(在渲染前)。
微任務包含:
Promise.then
Object.observe
MutaionObserver
process.nextTick(Node.js 環境)
運行機制
在事件循環中,每進行一次循環操作稱為 tick,每一次 tick 的任務處理模型是比較復雜的,但關鍵步驟如下:
- 執行一個宏任務(棧中沒有就從事件隊列中獲取)
- 執行過程中如果遇到微任務,就將它添加到微任務的任務隊列中
- 宏任務執行完畢后,立即執行當前微任務隊列中的所有微任務(依次執行)
- 當前宏任務執行完畢,開始檢查渲染,然后GUI線程接管渲染
- 渲染完畢后,JS線程繼續接管,開始下一個宏任務(從事件隊列中獲取)
如圖:
總結:
- tasks 按序執行,瀏覽器會在 tasks 之間執行渲染。
- microtasks 按序執行,在下面情況時執行:
- 在每個回調之后,只要沒有其它代碼正在運行。
- 在每個 task 的末尾
async/await
async/await本質上還是基於Promise的一些封裝,而Promise是屬於微任務的一種。所以在使用await關鍵字與Promise.then相同。
async函數在await之前的代碼都是同步執行的,可以理解為await之前的代碼屬於new Promise時傳入的代碼,await之后的所有代碼都是在Promise.then中的回調。
注意await的區別:
打印結果為
await之后的代碼必須等await語句執行完成后(包括微任務完成),才能執行后面的,也就是說,只有運行完await語句,才把await語句后面的全部代碼加入到微任務行列,所以,在遇到await promise時,必須等await promise函數執行完畢才能對await語句后面的全部代碼加入到微任務中。
總的來說,執行順序:
1、根據上下文執行同步數據
2、看到await,await+await后面的句子,同等於.then的回調。等正常的js執行完了以后,再走微任務await后的
4、然后是微任務.then后面的內容
6、宏任務(setimeout)
延伸:https://www.cnblogs.com/jiangyuzhen/p/11064408.html 里面很多面試題