JS 分為同步任務和異步任務
同步任務在主線程上執行
異步任務放在主線程之外的一個任務隊列
主線程執行完畢后,讀取任務隊列的內容
宏任務(macro)task:當前主線程上執行的就是一個宏任務。例: script 的代碼、setTimeout、setInterval、postMessage等。
微任務:microtask。例:Promise.then、await后面的代碼。
在執行當前宏任務時(同步執行時),遇到 setTimeout 會把它放到宏任務隊列。遇到Promise.then會放到微任務隊列。
當前 宏任務 執行完畢后,會先查看微任務隊列,如果有任務,優先執行,否則執行下一個宏任務。所以 promise.then 會先於 setTimeout執行。
試試這段代碼(頭條):
//請寫出輸出內容 async function async1() { console.log('async1 start'); await async2(); console.log('async1 end'); } async function async2() { console.log('async2'); } console.log('script start'); setTimeout(function() { console.log('setTimeout'); }, 0) async1(); new Promise(function(resolve) { console.log('promise1'); resolve(); }).then(function() { console.log('promise2'); }); console.log('script end');
//打印結果
/* script start async1 start async2 promise1 script end async1 end promise2 setTimeout */
根據上面的原則來,基本沒啥問題了。但是肯定有個疑惑的地方。為什么 “async1 end” 跑到了倒數第三個。
那是因為 async 函數中,遇到 await 會跳出當前函數,並讓出線程,再將await后面的代碼放到 微任務(microtask)隊列中。
整個執行過程:
1、同步執行, 輸出“script start”。
2、遇到setTimeout,將里面代碼放到宏任務隊列。
3、繼續往下,輸出 “async1 start”,然后執行 async2函數,輸出 “async2”。
4、async2執行完畢,將await async2 后面的代碼加入到 微任務隊列;
5、繼續執行,new Promise, 同步輸出“promise1”。
6、遇到 promise.then,加入到微任務隊列,
7、然后輸出 "script end"。
8、當前宏任務執行完畢,查看微任務隊列,按照先進先出原則。
9、依次輸出“async1 end”、“promise2”
10、執行下一個宏任務,里面只有一個setTimeout,所以最后輸出 “setTimeout”
同步執行的:[script start] [async1 start] [async2] [promise1] [script end]
微任務隊列:[async1 end] [ promise2 ]
宏任務隊列:[setTimeout]