宏任務(wù)與微任務(wù)

本文原創(chuàng):liuwan

在說(shuō)微任務(wù)與宏任務(wù)之前我們先說(shuō)一下同步任務(wù)與異步任務(wù)的概念吧陪每。

同步任務(wù)與異步任務(wù)

JavaScript語(yǔ)言的一大特點(diǎn)就是單線程,也就是說(shuō)筋帖,同一個(gè)時(shí)間只能做一件事哩都。單線程就意味著瞎颗,所有任務(wù)需要排隊(duì)月洛,前一個(gè)任務(wù)結(jié)束何恶,才會(huì)執(zhí)行后一個(gè)任務(wù)。如果前一個(gè)任務(wù)耗時(shí)很長(zhǎng)膊存,后一個(gè)任務(wù)就不得不一直等著导而。

如果排隊(duì)是因?yàn)橛?jì)算量大忱叭,CPU忙不過(guò)來(lái)隔崎,倒也算了今艺,但是很多時(shí)候CPU是閑著的,因?yàn)镮O設(shè)備(輸入輸出設(shè)備)很慢(比如Ajax操作從網(wǎng)絡(luò)讀取數(shù)據(jù))爵卒,不得不等著結(jié)果出來(lái)虚缎,再往下執(zhí)行。

JavaScript語(yǔ)言的設(shè)計(jì)者意識(shí)到钓株,這時(shí)主線程完全可以不管IO設(shè)備实牡,掛起處于等待中的任務(wù),先運(yùn)行排在后面的任務(wù)轴合。等到IO設(shè)備返回了結(jié)果创坞,再回過(guò)頭,把掛起的任務(wù)繼續(xù)執(zhí)行下去受葛。

于是题涨,所有任務(wù)可以分成兩種,一種是同步任務(wù)(synchronous)总滩,另一種是異步任務(wù)(asynchronous)纲堵。同步任務(wù)指的是,在主線程上排隊(duì)執(zhí)行的任務(wù)闰渔,只有前一個(gè)任務(wù)執(zhí)行完畢席函,才能執(zhí)行后一個(gè)任務(wù);異步任務(wù)指的是冈涧,不進(jìn)入主線程茂附、而進(jìn)入"任務(wù)隊(duì)列"(task queue)的任務(wù),只有"任務(wù)隊(duì)列"通知主線程督弓,某個(gè)異步任務(wù)可以執(zhí)行了营曼,該任務(wù)才會(huì)進(jìn)入主線程執(zhí)行。
具體來(lái)說(shuō)咽筋,異步執(zhí)行的運(yùn)行機(jī)制如下溶推。(同步執(zhí)行也是如此,因?yàn)樗梢员灰暈闆](méi)有異步任務(wù)的異步執(zhí)行奸攻。)
1.所有同步任務(wù)都在主線程上執(zhí)行蒜危,形成一個(gè)執(zhí)行棧(execution context stack)。
2.主線程之外睹耐,還存在一個(gè)"任務(wù)隊(duì)列"(task queue)辐赞。只要異步任務(wù)有了運(yùn)行結(jié)果,在"任務(wù)隊(duì)列"之中放置一個(gè)事件硝训。
3.一旦"執(zhí)行棧"中的所有同步任務(wù)執(zhí)行完畢响委,系統(tǒng)就會(huì)讀取"任務(wù)隊(duì)列"新思,看看里面有哪些事件。那些對(duì)應(yīng)的異步任務(wù)赘风,于是結(jié)束等待狀態(tài)夹囚,進(jìn)入執(zhí)行棧,開(kāi)始執(zhí)行邀窃。
4.主線程不斷重復(fù)上面的3荸哟。

以上摘自廖雪峰的博客 JavaScript 運(yùn)行機(jī)制詳解:再談Event Loop.

問(wèn)題

我們先看一下下面的代碼,然后思考一下輸出的先后順序

setTimeout(() =>{
    console.log('1')
});

new Promise((resolve) => {
    console.log('2');
    resolve();
}).then(() => {
    console.log('3')
});

console.log('4');

按照同步與異步的概念來(lái)看瞬捕,輸出順序應(yīng)該是2鞍历、4、1肪虎、3.
但是劣砍,打開(kāi)控制臺(tái),輸入代碼扇救,查看輸出刑枝,順序是這樣的2、4爵政、3仅讽、1,發(fā)生了什么钾挟?


我太難了

想知道發(fā)生了什么就繼續(xù)往下看吧洁灵。

宏任務(wù)與微任務(wù)

除了廣義的同步任務(wù)和異步任務(wù),我們對(duì)任務(wù)有更精細(xì)的定義掺出,分為宏任務(wù)和微任務(wù)徽千。

  1. 宏任務(wù):包括整體代碼script,setTimeout汤锨,setInterval双抽;
  2. 微任務(wù):Promise,process.nextTick

注:Promise立即執(zhí)行闲礼,then函數(shù)分發(fā)到“microtask”隊(duì)列牍汹,process.nextTick分發(fā)到“microtask”隊(duì)列
js引擎會(huì)把宏任務(wù)和微任務(wù)放置兩個(gè)“任務(wù)隊(duì)列”中,分別是“macrotask”隊(duì)列以及“microtask”隊(duì)列柬泽。在執(zhí)行異步任務(wù)時(shí)慎菲,先執(zhí)行宏任務(wù),然后在執(zhí)行微任務(wù)锨并。

所以現(xiàn)在解釋一下上面問(wèn)題中的輸出順序問(wèn)題:

  1. 這段代碼作為宏任務(wù)露该,進(jìn)入主線程
  2. 遇到setTimeout,把它的回掉函數(shù)放置“macrotask”隊(duì)列中第煮,然后接著執(zhí)行下面的代碼
  3. 遇到Promise解幼,new Promise會(huì)立即執(zhí)行抑党,于是輸出2,其then函數(shù)會(huì)被放置“microtask”隊(duì)列
  4. 遇到console.log('4')直接就執(zhí)行了
  5. 整體代碼script作為宏任務(wù)已經(jīng)執(zhí)行結(jié)束撵摆,判斷“microtask”隊(duì)列中是否有可執(zhí)行的微任務(wù)(then函數(shù))底靠,然后執(zhí)行,輸出3
  6. 至此台汇,整個(gè)代碼的第一輪循環(huán)結(jié)束了苛骨,要開(kāi)始下一輪循環(huán)篱瞎,先去查看“macrotask”隊(duì)列苟呐,有setTimeout的回掉函數(shù),然后執(zhí)行俐筋,執(zhí)行結(jié)束牵素,輸出1。
  7. 結(jié)束澄者。

所以上述問(wèn)題的輸出順序知道怎么肥死了吧笆呆?
盜一張事件循環(huán),宏任務(wù)粱挡,微任務(wù)的關(guān)系圖赠幕,如下:


在這里插入圖片描述

圖說(shuō)明:進(jìn)入整體代碼(宏任務(wù))后,開(kāi)始第一次循環(huán)询筏。接著執(zhí)行所有的微任務(wù)榕堰。然后再次從宏任務(wù)開(kāi)始,找到符合執(zhí)行條件的一個(gè)宏任務(wù)執(zhí)行完畢嫌套,再執(zhí)行所有的微任務(wù)逆屡。

復(fù)習(xí)一下上面的知識(shí)點(diǎn),我們瞅一眼以下代碼:

console.log('1');                           

setTimeout(() => {                         
    console.log('2');
    process.nextTick(() => {
        console.log('3');
    })
    setTimeout(() => {
        console.log('10')
        new Promise((resolve) => {
            console.log('11');
            resolve();
        }).then(() => {
            console.log('12')
        })
    })    
    new Promise((resolve) => {
        console.log('4');
        resolve();
    }).then(() => {
        console.log('5')
    })
})

process.nextTick(() => {
    console.log('6');
})

new Promise((resolve) => {        
    console.log('7');
    resolve();
}).then(() => {
    console.log('8')
    setTimeout(() => {
        console.log('9')
    })                 
})

console.log('10')

大聲說(shuō)出答案吧:1踱讨、7魏蔗、10、6痹筛、8莺治、2、4帚稠、3谣旁、5、9翁锡、10蔓挖、11、12
好吧馆衔,我們分析一下:

  1. 整段代碼作為宏任務(wù)瘟判,進(jìn)入主線程
  2. 遇到console.log('1')怨绣,立即執(zhí)行,并向下執(zhí)行
  3. 遇到setTimeout拷获,把它的回掉函數(shù)fn1篮撑,放置“macrotask”隊(duì)列中,接著執(zhí)行下面的代碼
  4. 遇到process.nextTick匆瓜,把其回調(diào)函數(shù)fn2放置“microtask”隊(duì)列
  5. 遇到Promise赢笨,new Promise會(huì)立即執(zhí)行,于是輸出7驮吱,其then函數(shù)fn3會(huì)被放置“microtask”隊(duì)列
  6. 遇到console.log('10')直接就執(zhí)行了
  7. 整體代碼script作為宏任務(wù)已經(jīng)執(zhí)行結(jié)束茧妒,判斷“microtask”隊(duì)列中是否有可執(zhí)行的微任務(wù)(fn2以及fn3),隊(duì)列具體先進(jìn)先出的特點(diǎn)左冬,所以先執(zhí)行fn2桐筏,輸出6,然后執(zhí)行fn3拇砰,輸出8梅忌,里面包含setTimeout,把它的回調(diào)函數(shù)fn4放置“macrotask”隊(duì)列中除破。
  8. 至此牧氮,整個(gè)代碼的第一輪循環(huán)結(jié)束了,要開(kāi)始下一輪循環(huán)」宸悖現(xiàn)在“macrotask”隊(duì)列中有fn1踱葛、fn4
  9. 先去查看“macrotask”隊(duì)列,先執(zhí)行fn1躁垛。
  10. 執(zhí)行fn1剖毯,遇到console.log('2'),就輸出教馆,遇到process.nextTick逊谋,將其回調(diào)函數(shù)fn5放置“microtask”隊(duì)列,遇到setTimeout土铺,把它的回掉函數(shù)fn6放置“macrotask”隊(duì)列中胶滋,遇到Promise,new Promise會(huì)立即執(zhí)行悲敷,于是輸出4究恤,其then函數(shù)fn7會(huì)被放置“microtask”隊(duì)列,即這個(gè)宏任務(wù)執(zhí)行完成后德。“macrotask”隊(duì)列里面有fn4理张、fn6雾叭。
  11. 現(xiàn)在檢查“microtask”隊(duì)列,里面有fn5暂幼、fn7移迫,把里面的任務(wù)全部執(zhí)行完畢起意,
    先執(zhí)行fn5,輸出3,再執(zhí)行fn7亲善,輸出5
  12. 至此逗柴,又一輪的循環(huán)結(jié)束了
  13. 再檢查“macrotask”隊(duì)列,里面有fn4琳水、fn6,執(zhí)行fn4造垛,輸出9.
  14. 而現(xiàn)在的“microtask”隊(duì)列是空的顶瞒,再檢查“macrotask”隊(duì)列赃绊,有fn6
  15. 執(zhí)行fn6羡榴,輸出10校仑,遇到new Promise者冤,輸出11涉枫,并把其回調(diào)函數(shù)fn8放置“microtask”隊(duì)列愿汰,至此宏任務(wù)fn6結(jié)束乐纸,
  16. 檢查“microtask”隊(duì)列汽绢,并執(zhí)行fn8,輸出12跌宛,至此疆拘,“macrotask”隊(duì)列以及“microtask”隊(duì)列全部空了寂曹。
  17. 結(jié)束。

參考文章

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市渺氧,隨后出現(xiàn)的幾起案子阶女,更是在濱河造成了極大的恐慌秃踩,老刑警劉巖,帶你破解...
    沈念sama閱讀 207,248評(píng)論 6 481
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件鸟赫,死亡現(xiàn)場(chǎng)離奇詭異台谢,居然都是意外死亡,警方通過(guò)查閱死者的電腦和手機(jī)朋沮,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,681評(píng)論 2 381
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái)塘慕,“玉大人,你說(shuō)我怎么就攤上這事图呢「蛑” “怎么了?”我有些...
    開(kāi)封第一講書(shū)人閱讀 153,443評(píng)論 0 344
  • 文/不壞的土叔 我叫張陵稚瘾,是天一觀的道長(zhǎng)。 經(jīng)常有香客問(wèn)我柱宦,道長(zhǎng)播瞳,這世上最難降的妖魔是什么赢乓? 我笑而不...
    開(kāi)封第一講書(shū)人閱讀 55,475評(píng)論 1 279
  • 正文 為了忘掉前任蚓炬,我火速辦了婚禮,結(jié)果婚禮上肯夏,老公的妹妹穿的比我還像新娘驯击。我一直安慰自己徊都,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 64,458評(píng)論 5 374
  • 文/花漫 我一把揭開(kāi)白布。 她就那樣靜靜地躺著袱耽,像睡著了一般朱巨。 火紅的嫁衣襯著肌膚如雪冀续。 梳的紋絲不亂的頭發(fā)上,一...
    開(kāi)封第一講書(shū)人閱讀 49,185評(píng)論 1 284
  • 那天钻蹬,我揣著相機(jī)與錄音问欠,去河邊找鬼顺献。 笑死枯怖,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的肿轨。 我是一名探鬼主播蕊程,決...
    沈念sama閱讀 38,451評(píng)論 3 401
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼槐沼,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼!你這毒婦竟也來(lái)了纽窟?” 一聲冷哼從身側(cè)響起臂港,我...
    開(kāi)封第一講書(shū)人閱讀 37,112評(píng)論 0 261
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤审孽,失蹤者是張志新(化名)和其女友劉穎佑力,沒(méi)想到半個(gè)月后打颤,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體漓滔,經(jīng)...
    沈念sama閱讀 43,609評(píng)論 1 300
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡透且,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 36,083評(píng)論 2 325
  • 正文 我和宋清朗相戀三年秽誊,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了养距。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 38,163評(píng)論 1 334
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖敬肚,靈堂內(nèi)的尸體忽然破棺而出毕荐,到底是詐尸還是另有隱情,我是刑警寧澤艳馒,帶...
    沈念sama閱讀 33,803評(píng)論 4 323
  • 正文 年R本政府宣布憎亚,位于F島的核電站员寇,受9級(jí)特大地震影響,放射性物質(zhì)發(fā)生泄漏第美。R本人自食惡果不足惜蝶锋,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,357評(píng)論 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望什往。 院中可真熱鬧扳缕,春花似錦、人聲如沸别威。這莊子的主人今日做“春日...
    開(kāi)封第一講書(shū)人閱讀 30,357評(píng)論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)。三九已至科侈,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間姑躲,已是汗流浹背屎开。 一陣腳步聲響...
    開(kāi)封第一講書(shū)人閱讀 31,590評(píng)論 1 261
  • 我被黑心中介騙來(lái)泰國(guó)打工, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留额划,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 45,636評(píng)論 2 355
  • 正文 我出身青樓,卻偏偏與公主長(zhǎng)得像,于是被迫代替她去往敵國(guó)和親。 傳聞我的和親對(duì)象是個(gè)殘疾皇子秕磷,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 42,925評(píng)論 2 344

推薦閱讀更多精彩內(nèi)容