爲何寫這篇文章?前端
由於這道題涉及了不少知識點:同步任務、異步任務、宏任務、微任務、任務隊列、執行棧、js運行機制、EventLoop,因此想整理一下,寫一篇文章,但願對小夥伴們有所幫助!node
下面這段promise、async和await代碼,請問控制檯打印順序?面試
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'); 複製代碼
正確答案promise
script start async1 start async2 promise1 script end async1 end promise2 setTimeout 複製代碼
注:由於是一道前端面試題,因此答案是以瀏覽器的eventloop機制爲準的,在node平臺上運行會有差別瀏覽器
相信大多數前端都知道,這道題考的就是js裏面的事件循環和任務隊列markdown
同步任務、異步任務、宏任務、微任務、任務隊列、執行棧、js運行機制、Event Loop異步
這題考察的是js中的事件循環和任務隊列,注意如下幾點:async
它經過返回一個 Promise 對象來返回結果最大的特色是:函數
經過 async / await 將異步的操做,但寫法和結構倒是和咱們平時寫的(同步代碼)是同樣
返回值(return_value):返回 Promise 對象的處理結果。若是等待的不是 Promise 對象,則返回該值自己,oop
因此,當await操做符後面的表達式是一個Promise的時候,它的返回值,實際上就是Promise的回調函數resolve的參數
咱們都知道Promise是一個當即執行函數,可是他的成功(或失敗:reject)的回調函數resolve倒是一個異步執行的回調。當執行到resolve()時,這個任務會被放入到回調隊列中,等待調用棧有空閒時事件循環再來取走它
async會返回Promise對象,若是返回值不是Promise對象則調用Promise resolve來換成Promise對象
async/await創建在Prmise機制上
總結一句話:帶async關鍵字的函數,它使得你的函數的返回值一定是 promise 對象上
這裏先簡單的說一些Event Loop的概念,在最近這段時間我會寫一篇關於Event Loop的文章
Javascript是單線程的,全部的同步任務都會在主線程中執行
主線程以外,還有一個任務隊列。每當一個異步任務有結果了,就往任務隊列裏塞一個事件。 當主線程中的任務,都執行完以後,系統會 「依次」 讀取任務隊列裏的事件。與之相對應的異步任務進入主線程,開始執行
異步任務之間,會存在差別,因此它們執行的優先級也會有區別。大體分爲 微任務(micro task,如:Promise、MutaionObserver等)和宏任務(macro task,如:setTimeout、setInterval、I/O等)。同一次事件循環中,微任務永遠在宏任務以前執行
主線程會不斷重複上面的步驟,直到執行完全部任務
第一步,輸出script start
雖然有兩個函數聲明,有async關鍵字,可是沒有調用咱們就不看,直接打印同步代碼console.log(‘script start’)
第二步,輸出async1 start
在執行async1這個函數的時候,async表達式定義的函數也是當即執行 在前面咱們說過看到帶有async關鍵字函數,不用慌,它僅僅是把return值包裝成了promise,因此就很普通的打印 console.log( 'async1 start' )
第三步,輸出async2
async2是async定義的函數,輸出async2並返回promise對象, await後,中斷async函數,先執行async外的同步代碼, 目前就直接打印 console.log('async2')
第四步,輸出promise1
執行new Promise(),Promise構造函數是直接調用的同步代碼,因此就打印console.log( 'promise1' )
第五步,輸出script end
由於上一步先打印了promise1,而後執行到resolve的時候,而後跳出promise繼續向下執行,因此就打印console.log( 'script end' )
第六步,輸出async1 end
由於await定義的這個promise已經執行完,而且返回結果,因此繼續執行async1函數後的任務,就是console.log(‘async1 end’)
第七步,輸出promise2
由於前面的new promise放進resolve回調,這個resolve被放到調用棧執行,因此就打印console.log('promise')
第八步,輸出setTimerout
最後執行定時器(宏任務)setTimeout,打印console.log( 'setTimerout' )
在這個變式中我將async2中的函數也變成了Promise函數,代碼以下:
async function async1() { console.log('async1 start'); await async2(); console.log('async1 end'); } async function async2() { //async2作出以下更改: new Promise(function(resolve) { console.log('promise1'); resolve(); }).then(function() { console.log('promise2'); }); } console.log('script start'); setTimeout(function() { console.log('setTimeout'); }, 0) async1(); new Promise(function(resolve) { console.log('promise3'); resolve(); }).then(function() { console.log('promise4'); }); console.log('script end'); 複製代碼
正確答案
script start async1 start promise1 promise3 script end promise2 async1 end promise4 setTimeout 複製代碼
我將async1中await後面的代碼和async2的代碼都改成異步的,代碼以下:
async function async1() { console.log('async1 start'); await async2(); //更改以下: setTimeout(function() { console.log('setTimeout1') },0) } async function async2() { //更改以下: setTimeout(function() { console.log('setTimeout2') },0) } console.log('script start'); setTimeout(function() { console.log('setTimeout3'); }, 0) async1(); new Promise(function(resolve) { console.log('promise1'); resolve(); }).then(function() { console.log('promise2'); }); console.log('script end'); 複製代碼
正確答案
script start async1 start promise1 script end promise2 setTimeout3 setTimeout2 setTimeout1 複製代碼
這道題總體來講與上面題大同小異與,代碼以下:
async function a1 () { console.log('a1 start') await a2() console.log('a1 end') } async function a2 () { console.log('a2') } console.log('script start') setTimeout(() => { console.log('setTimeout') }, 0) Promise.resolve().then(() => { console.log('promise1') }) a1() let promise2 = new Promise((resolve) => { resolve('promise2.then') console.log('promise2') }) promise2.then((res) => { console.log(res) Promise.resolve().then(() => { console.log('promise3') }) }) console.log('script end') 複製代碼
正確答案
script start a1 start a2 promise2 script end promise1 a1 end promise2.then promise3 setTimeout 複製代碼
若是以爲本文還不錯,記得點個贊哦!
歡迎你們加入,一塊兒學習前端,共同進步!