摘要:一篇文章和一道面試題最近,有篇名為張圖幫你一步步看清和的執(zhí)行順序的文章引起了我的關(guān)注。作者用一道年今日頭條的前端面試題為引子,分步講解了最終結(jié)果的執(zhí)行原因。從字面意思理解,讓我們等等。當(dāng)前的最新版本,在這里的執(zhí)行順序上,的確存在有問(wèn)題。
一篇文章和一道面試題
最近,有篇名為 《8張圖幫你一步步看清 async/await 和 promise 的執(zhí)行順序》 的文章引起了我的關(guān)注。
作者用一道2017年「今日頭條」的前端面試題為引子,分步講解了最終結(jié)果的執(zhí)行原因。其中涉及到了不少概念,比如異步的執(zhí)行順序,宏任務(wù),微任務(wù)等等,同時(shí)作者限定了執(zhí)行范圍,以瀏覽器的 event loop 機(jī)制為準(zhǔn)。下面是原題的代碼:
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");
緊接著,作者先給出了答案。并希望讀者先行自我測(cè)試。
script start async1 start async2 promise1 script end promise2 async1 end setTimeout
我在看這道題的時(shí)候,先按照自己的理解寫(xiě)出了結(jié)果。
script start async1 start async2 promise1 script end async1 end promise2 setTimeout一些重要的概念
這里需要先簡(jiǎn)單地說(shuō)一些 event loop 的概念。
Javascript是單線程的,所有的同步任務(wù)都會(huì)在主線程中執(zhí)行。
主線程之外,還有一個(gè)任務(wù)隊(duì)列。每當(dāng)一個(gè)異步任務(wù)有結(jié)果了,就往任務(wù)隊(duì)列里塞一個(gè)事件。
當(dāng)主線程中的任務(wù),都執(zhí)行完之后,系統(tǒng)會(huì) “依次” 讀取任務(wù)隊(duì)列里的事件。與之相對(duì)應(yīng)的異步任務(wù)進(jìn)入主線程,開(kāi)始執(zhí)行。
異步任務(wù)之間,會(huì)存在差異,所以它們執(zhí)行的優(yōu)先級(jí)也會(huì)有區(qū)別。大致分為 微任務(wù)(micro task,如:Promise、MutaionObserver等)和宏任務(wù)(macro task,如:setTimeout、setInterval、I/O等)。同一次事件循環(huán)中,微任務(wù)永遠(yuǎn)在宏任務(wù)之前執(zhí)行。
主線程會(huì)不斷重復(fù)上面的步驟,直到執(zhí)行完所有任務(wù)。
另外,還有 async/await 的概念。
async 函數(shù),可以理解為是Generator 函數(shù)的語(yǔ)法糖。
它建立在promise之上,總是與await一起使用的。
await會(huì)返回一個(gè)Promise 對(duì)象,或者一個(gè)表達(dá)式的值。
其目的是為了讓異步操作更優(yōu)雅,能像同步一樣地書(shū)寫(xiě)。
我的理解再說(shuō)說(shuō)我對(duì)這道題的理解。
首先,從console的數(shù)量上看,會(huì)輸出8行結(jié)果。
再瞟了一眼代碼,看到了setTimeout,于是,默默地把它填入第8行。
在setTimeout附近,看到了 console.log( "script start" ) 和 async1(),可以確認(rèn)它們是同步任務(wù),會(huì)先在主線程中執(zhí)行。所以,妥妥地在第1行填入 script start,第2行填入async1方法中的第一行 async1 start。
接下來(lái),遇到了await。從字面意思理解,讓我們等等。需要等待async2()函數(shù)的返回,同時(shí)會(huì)阻塞后面的代碼。所以,第3行填入 async2。
講道理,await都執(zhí)行完了,該輪到console.log( "async1 end" )的輸出了。但是,別忘了下面還有個(gè)Promise,有一點(diǎn)需要注意的是:當(dāng) new 一個(gè) Promise的時(shí)候,其 resolve 方法中的代碼會(huì)立即執(zhí)行。如果不是 async1()的 await 橫插一杠,promise1 可以排得更前面。所以,現(xiàn)在第4行填入 promise1。
再接下來(lái),同步任務(wù) console.log( "script end" ) 執(zhí)行。第5行填入 script end。
還有第6和第7行,未填。回顧一下上面提到 async/await 的概念,其目的是為了讓異步能像同步一樣地書(shū)寫(xiě)。那么,我認(rèn)為 console.log( "async1 end" ) 就是個(gè)同步任務(wù)。所以,第6行填入async1 end。
最后,順理成章地在第7行填入 promise2。
與作者答案的不同回過(guò)頭對(duì)比與作者的答案,發(fā)現(xiàn)第6和第7行的順序有問(wèn)題。
再耐心地往下看文章,反復(fù)地看了幾遍 async1 end 和 promise2 誰(shuí)先誰(shuí)后,還是無(wú)法理解為何在chrome瀏覽器中,promise2 會(huì)先于 async1 end 輸出。
然后,看到評(píng)論區(qū),發(fā)現(xiàn)也有人提出了相同的疑惑。@rhinel提出,在他的72.0.3622.0(正式版本)dev(64 位)的chrome中,跑出來(lái)的結(jié)果是 async1 end 在 promise2 之前。
隨即我想到了一種可能,JS的規(guī)范可能會(huì)在未來(lái)有變化。于是,我用自己的react工程試了一下(工程中的babel-loader版本為7.1.5。.babelrc的presets設(shè)置了stage-3),結(jié)果與我的理解一致。當(dāng)前的最新版本 chromeV71,在這里的執(zhí)行順序上,的確存在有問(wèn)題。
于是,我也在評(píng)論區(qū)給作者留了言,進(jìn)行了討論。@rhinel最后也證實(shí),其實(shí)最近才發(fā)布通過(guò)了這個(gè)順序的改進(jìn)方案,這篇 《Faster async functions and promises》 詳細(xì)解釋了這個(gè)改進(jìn),以及實(shí)現(xiàn)效果。不久之后,作者也在他文章的最后,補(bǔ)充了我們討論的結(jié)果,供讀者參考。
總結(jié)最后,我想說(shuō)的是,本文雖然只是由一道面試題引申出的,對(duì)瀏覽器執(zhí)行順序的思考、討論與驗(yàn)證的過(guò)程。但正是因?yàn)橛辛诉@些過(guò)程,才讓更多的思想得以碰撞,概念進(jìn)一步得以理解,規(guī)范得以明了。
有機(jī)會(huì)的話,希望能有與更多的同道,多多交流。
更新講道理,async/await 已經(jīng)出來(lái)挺久了,但在近期的面試中,凡是問(wèn)及異步操作,面試者的回答都還是 Promise,甚至知道 async/await 都很少,看來(lái)還有待進(jìn)一步普及。并不是說(shuō) Promise 有什么不好,只是覺(jué)得 async/await 用著挺爽的,希望能有更多的人用吧。只有用了,才能進(jìn)一步理解,產(chǎn)生更多的思考。
所以,這兩天翻出了之前寫(xiě)的關(guān)于什么是async函數(shù),及其相較于 Promise 的優(yōu)勢(shì)。重新整理了一下,原文請(qǐng)前往《細(xì)說(shuō)async/await相較于Promise的優(yōu)勢(shì)》。
希望對(duì)你有幫助,也期待進(jìn)一步的交流,感謝!
PS:歡迎關(guān)注我的公眾號(hào) “超哥前端小?!保涣鞲嗟南敕ㄅc技術(shù)。
文章版權(quán)歸作者所有,未經(jīng)允許請(qǐng)勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。
轉(zhuǎn)載請(qǐng)注明本文地址:http://www.ezyhdfw.cn/yun/100308.html
摘要:正如我標(biāo)題所說(shuō),簡(jiǎn)歷被拒??戳宋液?jiǎn)歷之后說(shuō)頭條競(jìng)爭(zhēng)激烈,我背景不夠,點(diǎn)到為止。。三準(zhǔn)備面試其實(shí)從三月份投遞簡(jiǎn)歷開(kāi)始準(zhǔn)備面試到四月份收,也不過(guò)個(gè)月的時(shí)間,但這都是建立在我過(guò)去一年的積累啊。 本文是 無(wú)精瘋 同學(xué)投稿的面試經(jīng)歷 關(guān)注微信公眾號(hào):進(jìn)擊的java程序員K,即可獲取最新BAT面試資料一份 在此感謝 無(wú)精瘋 同學(xué)的分享 目錄: 印象中的頭條 面試背景 準(zhǔn)備面試 ...
摘要:正如我標(biāo)題所說(shuō),簡(jiǎn)歷被拒??戳宋液?jiǎn)歷之后說(shuō)頭條競(jìng)爭(zhēng)激烈,我背景不夠,點(diǎn)到為止。。三準(zhǔn)備面試其實(shí)從三月份投遞簡(jiǎn)歷開(kāi)始準(zhǔn)備面試到四月份收,也不過(guò)個(gè)月的時(shí)間,但這都是建立在我過(guò)去一年的積累啊。 本文是 無(wú)精瘋 同學(xué)投稿的面試經(jīng)歷 關(guān)注微信公眾號(hào):進(jìn)擊的java程序員K,即可獲取最新BAT面試資料一份 在此感謝 無(wú)精瘋 同學(xué)的分享目錄:印象中的頭條面試背景準(zhǔn)備面試頭條一面(Java+項(xiàng)目)頭條...
摘要:在這里,如果用箭頭函數(shù),可以這樣改寫(xiě)箭頭函數(shù)并沒(méi)有自己的,所以事件處理函數(shù)的調(diào)用者并不受影響。比如,在需要?jiǎng)討B(tài)上下文的場(chǎng)景中,使用箭頭函數(shù)需要格外地小心,這些場(chǎng)景包括對(duì)象的方法原型方法事件的回調(diào)構(gòu)造函數(shù)。 showImg(https://segmentfault.com/img/bVboce6?w=1304&h=734); 前言 年味兒漸散,收拾下心情,繼續(xù)敲代碼吧。 對(duì)于即將到來(lái)金三...
摘要:?jiǎn)栴}的關(guān)鍵在于其執(zhí)行過(guò)程中的微任務(wù)數(shù)量,下文中我們需要用上述代碼中的方式對(duì)微任務(wù)的執(zhí)行順序進(jìn)行標(biāo)記,以輔助我們理解這其中的執(zhí)行過(guò)程。 原文發(fā)布在掘金社區(qū):https://juejin.im/post/5c3cc981f265da616a47e028 起源 2019年了,相信大家對(duì) Promise 和 async/await 都不再陌生了。 前幾日,我在社區(qū)讀到了一篇關(guān)于 async/...
摘要:但是有一個(gè)總的原則那就是總會(huì)指向,調(diào)用函數(shù)的那個(gè)對(duì)象。作為對(duì)象方法的調(diào)用函數(shù)作為某個(gè)對(duì)象的方法調(diào)用,這時(shí)就指這個(gè)上級(jí)對(duì)象。 showImg(https://segmentfault.com/img/bVbnvF7?w=750&h=422); 這是前端面試題系列的第 4 篇,你可能錯(cuò)過(guò)了前面的篇章,可以在這里找到: 偽類(lèi)與偽元素的區(qū)別及實(shí)戰(zhàn) 如何實(shí)現(xiàn)一個(gè)圣杯布局? 今日頭條 面試題和思...
閱讀 3313·2021-09-30 09:48
閱讀 3599·2021-09-22 16:00
閱讀 1142·2019-08-30 13:08
閱讀 3262·2019-08-30 10:53
閱讀 2490·2019-08-29 18:33
閱讀 1663·2019-08-29 12:47
閱讀 970·2019-08-29 12:16
閱讀 2007·2019-08-26 12:02