async 函數是 Generator 函數的語法糖。使用 關鍵字 async 來表示,在函數內部使用 await 來表示異步。想較於 Generator,Async 函數的改進在於下面四點:promise
await命令:正常狀況下,await命令後面是一個 Promise 對象,返回該對象的結果。若是不是 Promise 對象,就直接返回對應的值 瀏覽器
下面給你們看一道以前看過的題:網絡
function test1() { console.log("執行test1"); return "test1"; } function test2() { console.log("執行test2"); return Promise.resolve("hello test2"); } async function asyncTest() { console.log("asyncTest start..."); const v1 = await test1(); console.log(v1); const v2 = await test2(); console.log(v2); console.log(v1, v2); } setTimeout(function(){ console.log('setTimeout') },0) asyncTest(); new Promise(function(resolve){ console.log('promise1') resolve(); }).then(function(){ console.log('promise2') }) console.log('test end')
這道題結合了setTimeout、async、promise異步函數,根據三種不一樣異步任務執行順序能夠學習js引擎的事件循環機制,我們先看下結果:數據結構
test start... 執行test1 promise1 test end test1 執行test2 promise2 hello test2 test1,hello test2 setTimeout
再講答案以前先理解如下幾個概念:異步
事件循環與消息隊列 async
JS引擎線程遇到異步(DOM事件監聽、網絡請求、setTimeout計時器等...),會交給相應的線程單獨去維護異步任務,等待某個時機(計時器結束、網絡請求成功、用戶點擊DOM),而後由 事件觸發線程 將異步對應的 回調函數 加入到消息隊列中,消息隊列中的回調函數等待被執行。函數
同時,JS引擎線程會維護一個 執行棧,同步代碼會依次加入執行棧而後執行,結束會退出執行棧。oop
若是執行棧裏的任務執行完成,即執行棧爲空的時候(即JS引擎線程空閒),事件觸發線程纔會從消息隊列取出一個任務(即異步的回調函數)放入執行棧中執行。學習
消息隊列是相似隊列的數據結構,遵循**先入先出(FIFO)**的規則。
執行完了後,執行棧再次爲空,事件觸發線程會重複上一步操做,再取出一個消息隊列中的任務,這種機制就被稱爲事件循環(event loop)機制。線程
主代碼塊(script)依次加入執行棧,依次執行,主代碼塊爲:
宏任務與微任務
macrotask(宏任務) :主代碼塊、setTimeout、setInterval等(能夠看到,事件隊列中的每個事件都是一個 macrotask,如今稱之爲宏任務隊列
和 microtask(微任務):Promise、process.nextTick等
JS引擎線程首先執行主代碼塊。
每次執行棧執行的代碼就是一個宏任務,包括任務隊列(宏任務隊列)中的,由於執行棧中的宏任務執行完會去取任務隊列(宏任務隊列)中的任務加入執行棧中,即一樣是事件循環的機制。
在執行宏任務時遇到Promise等,會建立微任務(.then()裏面的回調),並加入到微任務隊列隊尾。
microtask必然是在某個宏任務執行的時候建立的,而在下一個宏任務開始以前,瀏覽器會對頁面從新渲染(task >> 渲染 >> 下一個task(從任務隊列中取一個))。同時,在上一個宏任務執行完成後,渲染頁面以前,會執行當前微任務隊列中的全部微任務。
也就是說,在某一個macrotask執行完後,在從新渲染與開始下一個宏任務以前,就會將在它執行期間產生的全部microtask都執行完畢(在渲染前)。
執行機制:
遇到異步函數 setTimeout,交給定時器觸發線程 setTimeout加入宏任務隊列,JS引擎線程繼續,出棧;
執行異步函數asyncTest,首先打印test start...
執行await test1函數首先打印"執行test1",await讓出線程去執行後面的代碼;
執行Promise 首先打印promise1,then後面函數爲微任務,添加到微任務隊列中
JS引擎線程繼續向下執行同步代碼console.log('test end')打印'test end'
回到asyncTest執行await test1因爲返回不是promise對象,因此直接返回test1
執行await test2()一樣先打印 "執行test2",因爲test2返回promise對象 會加入到以前微任務隊列中,await繼續讓出
執行微任務隊列,因爲任務隊列遵循先進先出結果,因此首先打印promise2,而後打印hello test2
微任務隊列執行完成後繼續執行asyncTest內 await以後的代碼打印 倆個await返回的值 --test1,hello test2
最後回到宏任務隊列執行setTimeout,打印setTimeout
若是我把test1變成異步函數,你們再思考一下會打印什麼結果:
async function test1() { console.log("執行test1"); return "test1"; } function test2() { console.log("執行test2"); return Promise.resolve("hello test2"); } async function asyncTest() { console.log("asyncTest start..."); const v1 = await test1(); console.log(v1); const v2 = await test2(); console.log(v2); console.log(v1, v2); } setTimeout(function(){ console.log('setTimeout') },0) asyncTest(); new Promise(function(resolve){ console.log('promise1') resolve(); }).then(function(){ console.log('promise2') }) console.log('test end')
以上就是此代碼執行過程,因爲本人也是在學習總結中,若有不對的地方請指教,共同窗習,一塊兒進步!!!