一道經典的前端筆試題,你能一眼寫出他們的執行結果嗎?
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個問題,那麼這道筆試題也就容易理解了
最終結果: