一道經典的前端筆試題,你能一眼寫出他們的執行結果嗎?
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');
首先第一個問題: JavaScript運行機制是什么?
詳細可參考:https://baijiahao.baidu.com/s?id=1615713540466951098&wfr=spider&for=pc
總結幾點就是:
- JavaScript語言是單線程的,同一個時間只能做一件事;
- 遵循事件循環機制,當JS解析執行時,會被引擎分為兩類任務,同步任務(synchronous) 和 異步任務(asynchronous)。對於同步任務來說,會被推到執行棧按順序去執行這些任務。對於異步任務來說,當其可以被執行時,會被放到一個 任務隊列(task queue) 里等待JS引擎去執行。當執行棧中的所有同步任務完成后,JS引擎才會去任務隊列里查看是否有任務存在,並將任務放到執行棧中去執行,執行完了又會去任務隊列里查看是否有已經可以執行的任務。這種循環檢查的機制,就叫做事件循環(Event Loop)。對於任務隊列,其實是有更細的分類。其被分為 微任務(microtask)隊列 & 宏任務(macrotask)隊列。
第二個問題:Promise的原理和運行機制是什么?
古人雲:“君子一諾千金”,這種“承諾將來會執行”的對象在JavaScript中稱為Promise對象。
Promise 是異步編程的一種解決方案,其實是一個構造函數,自己身上有all、reject、resolve這幾個方法,原型上有then、catch等方法。
參考:https://blog.csdn.net/qq_37860963/article/details/81539118
這里擴展一個問題:什么是異步呢?
同步就是一件事一件事的執行。只有前一個任務執行完畢,才能執行后一個任務。
js代碼只能一行一行的執行,不能在同一時間執行多個js代碼任務,這就導致如果有一段耗時較長的計算,或者是一個ajax請求等IO操作,如果沒有異步的存在,就會出現用戶長時間等待,並且由於當前任務還未完成,所以這時候所有的其他操作都會無響應,這時候就需要異步任務。
參考:https://blog.csdn.net/li123128/article/details/80650256
Promise運行順序總結:
- promise的構造函數是同步執行,promise.then中的函數是異步執行。
- 構造函數中的 resolve 或 reject 只有第一次執行有效,多次調用沒有任何作用。promise狀態一旦改變則不能再變。
- promise 的 .then 或者 .catch 可以被調用多次,但這里 Promise 構造函數只執行一次。或者說 promise 內部狀態一經改變,並且有了一個值,那么后續每次調用 .then 或者 .catch 都會直接拿到該值。
- 如果在一個then()中沒有返回一個新的promise,則 return 什么下一個then就接受什么,如果then中沒有return,則默認return的是 undefined.
- then()的嵌套會先將內部的then()執行完畢再繼續執行外部的then();
- catch和then的連用,如果每一步都有可能出現錯誤,那么就可能出現catch后面接上then的情況。如果在catch中也拋出了錯誤,則后面的then的第一個函數不會執行,因為返回的 promise狀態已經為rejected了
第三個問題:async、await執行順序?
什么是Async/Await?
- async/await是寫異步代碼的新方式,以前的方法有回調函數和Promise。
- async/await是基於Promise實現的,它不能用於普通的回調函數。
- async/await與Promise一樣,是非阻塞的。
- async/await使得異步代碼看起來像同步代碼,這正是它的魔力所在。
- await關鍵字只能用在aync定義的函數內。async函數會隱式地返回一個promise,該promise的reosolve值就是函數return的值。
執行順序:
使用 async 定義的函數,當它被調用時,它返回的其實是一個 Promise 對象。(當這個 async 函數返回一個值時,Promise 的 resolve 方法會負責傳遞這個值;當 async 函數拋出異常時,Promise 的 reject 方法也會傳遞這個異常值。)
await是一個讓出線程的標志。await后面的函數會先執行一遍,然后就會跳出整個async函數來執行后面js棧的代碼,等本輪事件循環執行完了之后又會跳回到async函數中等待await后面表達式的返回值,如果返回值為非promise則繼續執行async函數后面的代碼,否則將返回的promise放入promise隊列。
參考:https://segmentfault.com/a/1190000011296839
問題四:setTimeout的執行?
setTimeout和Promise一樣也是異步的
宏任務一般包括:整體代碼script,setTimeout,setInterval。
微任務:Promise,process.nextTick
微任務執行優先級高於宏任務,所以Promise比setTimeout優先執行。
理解了以上4個問題,那么這道筆試題也就容易理解了

最終結果:

