Javascript中Microtask和Macrotask鮮為人知的知識(shí)點(diǎn)
首先我們來(lái)看一道題目,如下javascript代碼,執(zhí)行后會(huì)在控制臺(tái)打印出什么內(nèi)容?
async function async1() { console.log('async1 start'); await async2(); console.log('async1 end'); } async function async2() { console.log('async2 start'); return new Promise((resolve, reject) => { resolve(); console.log('async2 promise'); }) } console.log('script start'); setTimeout(function() { console.log('setTimeout'); }, 0); async1(); new Promise(function(resolve) { console.log('promise1'); resolve(); }).then(function() { console.log('promise2'); }).then(function() { console.log('promise3'); }); console.log('script end')
說(shuō)實(shí)話,真正能在面試中把這道題目答對(duì)的前端工程師鳳毛麟角。我們先來(lái)瞧一下答案吧。把以上代碼存到test.js文件中,并用node執(zhí)行一下,結(jié)果如下:
如果把以上代碼貼到一個(gè)網(wǎng)頁(yè)中的script標(biāo)簽里面,然后打開(kāi)這個(gè)網(wǎng)頁(yè),再打開(kāi)控制臺(tái),可以看到如下輸出(Chrome 64位 63.0.3239.84):
結(jié)果和node打印的一模一樣。那么為什么是這個(gè)順序呢?
我們都知道js的單線程特性(html5的web worker不算在內(nèi)~)以及良好的異步支持。在單線程的前提下,異步任務(wù)到底什么時(shí)候開(kāi)始執(zhí)行,其實(shí)是有兩個(gè)隊(duì)列來(lái)進(jìn)行管理,即Macrotask和Microtask(只有一個(gè)字母的差距,不要認(rèn)錯(cuò)……)。在當(dāng)前正在執(zhí)行的線程中,如果碰到屬于Macrotask的異步任務(wù),則放入Macrotask隊(duì)列;碰到Microtask的異步任務(wù)則放入Microtask隊(duì)列。注意這里只是把任務(wù)放入隊(duì)列,并不會(huì)執(zhí)行它。等到當(dāng)前主線程任務(wù)執(zhí)行完畢之后,會(huì)依次從Microtask隊(duì)列中取出任務(wù)執(zhí)行,在執(zhí)行期間當(dāng)然還是遵循碰到異步任務(wù)放入相應(yīng)隊(duì)列的原則。等到Microtask任務(wù)全部執(zhí)行過(guò)了,此時(shí)再?gòu)腗acrotask隊(duì)列中取出一個(gè)任務(wù)執(zhí)行。
屬于Macrotask的任務(wù)有:
setTimeout,setInteveral,script標(biāo)簽,I/O,UI渲染
屬于Microtask的任務(wù)有:
Promise,async/await,process.nextTick,Object.observe,MutationObserver
(事實(shí)上,即使同樣是Microtask,內(nèi)部也是有優(yōu)先級(jí)的差別的,例如NodeJS的實(shí)現(xiàn)上,process.nextTick比Promise要先執(zhí)行。相關(guān)問(wèn)題可以瞧瞧這個(gè)連接:https://jakearchibald.com/2015/tasks-microtasks-queues-and-schedules/ 。反正我瞧到一半就放棄了,好在async/await和Promise沒(méi)有優(yōu)先級(jí)差別)
然后我們來(lái)分析一下本題中的執(zhí)行順序:
【1】第15行執(zhí)行,打印出script start
【2】第16至18行,把回調(diào)任務(wù)放入Macrotask (目前Macrotask:第16行setTimeout,Microtask:空)
【3】第20行,執(zhí)行async1函數(shù),先打印出第2行的async1 start
【4】第3行的async2先執(zhí)行,打印出第8行的async2 start
【5】第9行至第12行遇到Promise,先打印出第11行的async2 promise(注意不管你resolve寫(xiě)在new Promise的函數(shù)什么位置,都跟寫(xiě)到最后一句一樣?。?/p>
【6】第3行的async2返回了Promise,并且async2前面有await修飾,因此后面第4行的任務(wù)被放到Microtask(目前Macrotask:第16行setTimeout,Microtask:第4行)
【7】第22至25行,打印出promise1,并把第26行放入Microtask,注意第28行還沒(méi)執(zhí)行到,所以這行什么都不做(目前Macrotask:第16行setTimeout,Microtask:第4行,第26行)
【8】第30行打印script end(目前Macrotask:第16行setTimeout,Microtask:第4行,第26行)
【9】腳本主線程執(zhí)行結(jié)束,現(xiàn)在拿出來(lái)一個(gè)Microtask,即第4行,打印async1 end(目前Macrotask:第16行setTimeout,Microtask:第26行)
【10】再拿出來(lái)一個(gè)Microtask,即第26行,打印promise2,此時(shí)由于第26行后面跟著then,所以把第28行插入Microtask(目前Macrotask:第16行setTimeout,Microtask:第28行)
【11】再拿出來(lái)一個(gè)Microtask,即第28行,打印promise3(目前Macrotask:第16行的setTimeout,Microtask:空)
【12】Microtask沒(méi)有了,執(zhí)行下一個(gè)Macrotask,即第16行的setTimeout,打印setTimeout,結(jié)束
需要注意的是,以下兩種寫(xiě)法,效果是一模一樣的(resolve的位置無(wú)所謂):
寫(xiě)法1: new Promise((resolve, reject) => { console.log('1111'); resolve(); console.log('2222'); }); 寫(xiě)法2: new Promise((resolve, reject) => { console.log('1111'); console.log('2222'); resolve(); });
另外,對(duì)于Promise的鏈?zhǔn)秸{(diào)用,如new Promise(....).then(...).then(...)....,一次只放第一個(gè)then的內(nèi)容進(jìn)入Microtask,等第一個(gè)then執(zhí)行的時(shí)候,會(huì)把第二個(gè)then放入Microtask,而不是一次把兩個(gè)then都放進(jìn)去。
以上就是Javascript中Microtask和Macrotask鮮為人知的知識(shí)點(diǎn)的詳細(xì)內(nèi)容,更多關(guān)于Javascript中Microtask和Macrotask的資料請(qǐng)關(guān)注腳本之家其它相關(guān)文章!
相關(guān)文章
JS格式化字符串的兩種方法(反引號(hào)與String.prototype)
本文一共介紹了兩種實(shí)現(xiàn)方式,使用反引號(hào)或自定義方法實(shí)現(xiàn),需要的朋友可以參考下2023-06-06JavaScript實(shí)現(xiàn)滾動(dòng)加載更多
這篇文章主要為大家詳細(xì)介紹了JavaScript實(shí)現(xiàn)滾動(dòng)加載更多,文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2020-12-12JS實(shí)現(xiàn)在線Excel的附件上傳與下載
在本地使用Excel時(shí),經(jīng)常會(huì)有需要在Excel中添加一些附件文件的需求,今天小編將為大家介紹如何使用前端HTML+JS+CSS技術(shù)通過(guò)超鏈接單元格的形式實(shí)現(xiàn)在線Excel附件上傳、下載和修改的操作,需要的可以參考下2023-08-08JavaScript基礎(chǔ)進(jìn)階之?dāng)?shù)組方法總結(jié)(推薦)
下面小編就為大家?guī)?lái)一篇JavaScript基礎(chǔ)進(jìn)階之?dāng)?shù)組方法總結(jié)(推薦)。小編覺(jué)得挺不錯(cuò)的,現(xiàn)在就分享給大家,也給大家做個(gè)參考。一起跟隨小編過(guò)來(lái)看看吧2017-09-09Javascript前端UI框架Kit使用指南之Kitjs簡(jiǎn)介
本文給大家簡(jiǎn)單介紹了一款優(yōu)秀的Javascript前端UI框架--Kitjs,支持PC端以及手機(jī)開(kāi)發(fā)領(lǐng)域,非常的全面,這里推薦給有需要的小伙伴。2014-11-11基于three.js編寫(xiě)的一個(gè)項(xiàng)目類示例代碼
這篇文章主要給大家介紹了關(guān)于基于three.js編寫(xiě)的一個(gè)項(xiàng)目類的相關(guān)資料,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧。2018-01-01JavaScript 俄羅斯方塊游戲?qū)崿F(xiàn)方法與代碼解釋
這篇文章主要介紹了JavaScript 俄羅斯方塊游戲,結(jié)合實(shí)例形式詳細(xì)分析了JavaScript 俄羅斯方塊游戲原理、實(shí)現(xiàn)步驟及相關(guān)操作注意事項(xiàng),需要的朋友可以參考下2020-04-04詳解js對(duì)象中屬性的兩種類型之?dāng)?shù)據(jù)屬性和訪問(wèn)器屬性
在理解vue底層響應(yīng)式原理時(shí),了解到,原來(lái)對(duì)象中的屬性,不單單從表面看起來(lái)那么簡(jiǎn)單是key:value形式,而是還有隱藏的內(nèi)部特性,其中對(duì)象內(nèi)的屬性分為兩種類型的屬性:數(shù)據(jù)屬性和訪問(wèn)器屬性,本文將給大家詳細(xì)介紹一下數(shù)據(jù)屬性和訪問(wèn)器屬性,需要的朋友可以參考下2023-05-05javascript Xml增刪改查(IE下)操作實(shí)現(xiàn)代碼
比較不錯(cuò)的實(shí)現(xiàn)代碼,大家可以仔細(xì)的看下,思路。2009-01-01