圖解搞懂JavaScript引擎Event Loop

[圖片上傳失敗...(image-c3e3cb-1516440392143)]

一练对,js單線程存在的問(wèn)題

js是單線程的耙箍,處理任務(wù)是一件接著一件處理,所以如果一個(gè)任務(wù)需要處理很久的話,后面的任務(wù)就會(huì)被阻塞

所以js通過(guò)Event Loop事件循環(huán)的方式解決了這個(gè)問(wèn)題,在了解事件循環(huán)前扮饶,我們需要了解一些關(guān)鍵詞

二,什么是stack,queue乍构,heap甜无,event loop

  • stack(棧):吃多了吐
  • queue(隊(duì)列):吃多了...釋放
  • heap(堆):存儲(chǔ)obj對(duì)象

執(zhí)行棧

[圖片上傳失敗...(image-252451-1516440392143)]

js引擎運(yùn)行時(shí),當(dāng)代碼開始運(yùn)行的時(shí)候,會(huì)將代碼毫蚓,壓入執(zhí)行棧進(jìn)行執(zhí)行

例:
[圖片上傳失敗...(image-e1a3e3-1516440392143)]

當(dāng)代碼被解析后占键,函數(shù)會(huì)依次被壓入到棧中

[圖片上傳失敗...(image-fd2bff-1516440392143)]

有入棧,就要有出棧元潘,當(dāng)函數(shù)c執(zhí)行完畔乙,開始出棧

[圖片上傳失敗...(image-319c57-1516440392143)]

當(dāng)執(zhí)行棧遇到異步

前面執(zhí)行棧,先入后出翩概,但其實(shí)也是同步的牲距,同步就意味著會(huì)阻塞,所以需要異步钥庇,那當(dāng)執(zhí)行棧中出現(xiàn)異步代碼會(huì)怎么樣

[圖片上傳失敗...(image-49169d-1516440392143)]

此時(shí)在代碼中牍鞠,添加點(diǎn)擊事件和setTimeout,現(xiàn)在觀察一下執(zhí)行順序

[圖片上傳失敗...(image-8ba8f-1516440392143)]

觀察此時(shí)的執(zhí)行棧效果评姨,和上面的函數(shù)嵌套有顯著區(qū)別

1难述,console.log("sync")的語(yǔ)句,不會(huì)被壓入到執(zhí)行棧底部吐句,因?yàn)閏onsole已經(jīng)執(zhí)行結(jié)束了

2胁后,click和settimeout都入棧了,但它們內(nèi)部的console沒有入棧的嗦枢,這說(shuō)明他們沒有執(zhí)行完

3攀芯,如果click沒有執(zhí)行完,那為什么setTimeout會(huì)入棧文虏,不應(yīng)該被阻塞嗎侣诺?

答案是:當(dāng)瀏覽器在執(zhí)行棧執(zhí)行的時(shí)候,發(fā)現(xiàn)有異步任務(wù)之后氧秘,會(huì)交給webapi去維護(hù)年鸳,而執(zhí)行棧則繼續(xù)執(zhí)行后面的任務(wù)

[圖片上傳失敗...(image-12e78f-1516440392143)]

同樣,setTimeout同樣會(huì)被添加到webapi中

[圖片上傳失敗...(image-ef10b2-1516440392143)]

webapi是瀏覽器自己實(shí)現(xiàn)的功能敏储,這里專門維護(hù)事件阻星。

上面setTimeout旁邊有個(gè)進(jìn)度條,這個(gè)進(jìn)度就是設(shè)置的等待時(shí)間

回調(diào)隊(duì)列callback queue

上面的例子已添,當(dāng)setTimeout執(zhí)行結(jié)束的時(shí)候妥箕,是不是就應(yīng)該回到執(zhí)行棧,進(jìn)行執(zhí)行輸出呢更舞?

答案:并不是畦幢!

[圖片上傳失敗...(image-4f8ba-1516440392143)]

此時(shí),倒計(jì)時(shí)結(jié)束后的setTimeout的可執(zhí)行函數(shù)缆蝉,被放如了回調(diào)隊(duì)列

最后宇葱,setTimeout的可執(zhí)行函數(shù)瘦真,被從回調(diào)隊(duì)列中取出,再此放入了執(zhí)行棧

[圖片上傳失敗...(image-47b36b-1516440392143)]

這樣的執(zhí)行過(guò)程就叫 event loop事件循環(huán)

Event Loop的具體流程

執(zhí)行棧任務(wù)清空后黍瞧,才會(huì)從回調(diào)隊(duì)列頭部取出一個(gè)任務(wù)

[圖片上傳失敗...(image-1fb582-1516440392143)]

上面是一個(gè)最簡(jiǎn)單的例子诸尽,輸出結(jié)果是1,3印颤,2

這是為什么您机?

[圖片上傳失敗...(image-d98bb7-1516440392143)]

上圖展示了具體的執(zhí)行順序:

1,console.log(1)被壓入執(zhí)行棧

2年局,setTimeout在執(zhí)行棧被識(shí)別為異步任務(wù)际看,放入webapis中

3,console.log(3)被壓入執(zhí)行棧矢否,此時(shí)setTimeout的可執(zhí)行代碼還在回調(diào)隊(duì)列里等待

4仲闽,console.log(3)執(zhí)行完成后,從回調(diào)隊(duì)列頭部取出console.log(2)僵朗,放入執(zhí)行棧

5赖欣,console.log(2)執(zhí)行

回調(diào)隊(duì)列先進(jìn)先出

需要格外注意,回調(diào)隊(duì)列是先進(jìn)先出的验庙,例:
[圖片上傳失敗...(image-688aee-1516440392143)]

[圖片上傳失敗...(image-b475c7-1516440392143)]

當(dāng)console.log(4)執(zhí)行完成后畏鼓,從回調(diào)隊(duì)列里取出了console.log(2);

注意:只有console.log(2)執(zhí)行完成,執(zhí)行棧再次清空時(shí)壶谒,才會(huì)從回調(diào)隊(duì)列取出console.log(3)

測(cè)試概念是否正確

[圖片上傳失敗...(image-aa146a-1516440392143)]

上面的代碼最后輸出1,5膳沽,2汗菜,4,3挑社,執(zhí)行過(guò)程:

1陨界,輸出1,將2push進(jìn)回調(diào)隊(duì)列

2痛阻,將4push進(jìn)回調(diào)隊(duì)列

3菌瘪,輸出5

4,清空了執(zhí)行棧阱当,讀取輸出2俏扩,發(fā)現(xiàn)有3,將3push進(jìn)回調(diào)隊(duì)列

5弊添,清空了執(zhí)行棧录淡,讀取輸出4

6,清空了執(zhí)行棧油坝,讀取輸出3

至此嫉戚,看起來(lái)好像沒問(wèn)題了刨裆,但是!1蛱础7小!G系邸努潘!,事情還沒有結(jié)束

Macrotask(宏任務(wù))盯桦、Microtask(微任務(wù))

通過(guò)上面的例子慈俯,想必已經(jīng)對(duì)event loop有了一定的了解,現(xiàn)在繼續(xù)看一個(gè)例子

console.log(1);
setTimeout(()=>{
    console.log(2)
})
var p = new Promise((resolve,reject)=>{
    console.log(3)
    resolve("成功")
})
p.then(()=>{
    console.log(4)
})
console.log(5)

按照event loop的概念拥峦,應(yīng)該是1贴膘,3,5略号,2刑峡,4,因?yàn)閟etTimeout和then會(huì)被放到回調(diào)隊(duì)列里玄柠,然后又是先進(jìn)先出突梦,所以應(yīng)該是2先輸出,4后輸出

但事實(shí)輸出的順序是1羽利,3宫患,5,4这弧,2娃闲!

[圖片上傳失敗...(image-9fefe9-1516440392143)]

這是因?yàn)閜romise的then方法,被認(rèn)為是在Microtask微任務(wù)隊(duì)列當(dāng)中

什么是Macrotask(宏任務(wù))

Macrotask(宏任務(wù))很好理解匾浪,就是咱們前面介紹過(guò)的回調(diào)隊(duì)列callback queue

什么是Microtask(微任務(wù))

Microtask(微任務(wù))同樣是一個(gè)任務(wù)隊(duì)列皇帮,這個(gè)隊(duì)列的執(zhí)行順序是在清空?qǐng)?zhí)行棧之后

用圖展示就是

[圖片上傳失敗...(image-99e831-1516440392143)]

可以看到Macrotask(宏任務(wù))也就是回調(diào)隊(duì)列上面還有一個(gè)Microtask(微任務(wù))

Microtask(微任務(wù))雖然是隊(duì)列,但并不是一個(gè)一個(gè)放入執(zhí)行棧蛋辈,而是當(dāng)執(zhí)行棧請(qǐng)空属拾,會(huì)執(zhí)行全部Microtask(微任務(wù))隊(duì)列中的任務(wù),最后才是取回調(diào)隊(duì)列的第一個(gè)Macrotask(宏任務(wù))

例:

[圖片上傳失敗...(image-5e26e-1516440392143)]

上面的執(zhí)行過(guò)程是:

1冷溶,將setTimeout給push進(jìn)宏任務(wù)

2渐白,將then(2)push進(jìn)微任務(wù)

3,將then(4)push進(jìn)微任務(wù)

4逞频,任務(wù)隊(duì)列為空礼预,取出微任務(wù)第一個(gè)then(2)壓入執(zhí)行棧

5,輸出2虏劲,將then(3)push進(jìn)微任務(wù)

6托酸,任務(wù)隊(duì)列為空褒颈,取出微任務(wù)第一個(gè)then(4)壓入執(zhí)行棧

7,輸出4

8励堡,任務(wù)隊(duì)列為空谷丸,取出微任務(wù)第一個(gè)then(3)壓入執(zhí)行棧

9,輸出3

10应结,任務(wù)隊(duì)列為空刨疼,微任務(wù)也為空,取出宏任務(wù)中的setTimeout(1)

11鹅龄,輸出1

為什么then是微任務(wù)

這和每個(gè)瀏覽器有關(guān)揩慕,每個(gè)瀏覽器實(shí)現(xiàn)的promise不同,有的then是宏任務(wù)扮休,有的是微任務(wù)迎卤,chrome是微任務(wù),普遍都默認(rèn)為微任務(wù)

除了then以外玷坠,還有幾個(gè)事件也被記為微任務(wù):

  • process.nextTick
  • promises
  • Object.observe
  • MutationObserver
console.log("start");
setImmediate(()=>{
    console.log(1)
})
Promise.resolve().then(()=>{
    console.log(4);
})
Promise.resolve().then(()=>{
    console.log(5);
})
process.nextTick(function foo() {
    console.log(2);
});
process.nextTick(function foo() {
    console.log(3);
});
console.log("end")

上面代碼輸出start,end,2,3,4,5,1

process.nextTick的概念和then不太一樣蜗搔,process.nextTick是加入到執(zhí)行棧底部,所以和其他的表現(xiàn)并不一致

最后的測(cè)試

console.log("1");
setTimeout(()=>{
    console.log(2)
    Promise.resolve().then(()=>{
        console.log(3);
        process.nextTick(function foo() {
            console.log(4);
        });
    })
})
Promise.resolve().then(()=>{
    console.log(5);    
    setTimeout(()=>{
        console.log(6)
    })
    Promise.resolve().then(()=>{
        console.log(7);
    })
})

process.nextTick(function foo() {
    console.log(8);
    process.nextTick(function foo() {
        console.log(9);
    });
});
console.log("10")

執(zhí)行順序:

1八堡,輸出1

2樟凄,將setTimeout(2)push進(jìn)宏任務(wù)

3,將then(5)push進(jìn)微任務(wù)

4兄渺,在執(zhí)行棧底部添加nextTick(8)

5缝龄,輸出10

6,執(zhí)行nextTick(8)

7挂谍,輸出8

8二拐,在執(zhí)行棧底部添加nextTick(9)

9,輸出9

10凳兵,執(zhí)行微任務(wù)then(5)

11,輸出5

12企软,將setTimeout(6)push進(jìn)宏任務(wù)

13庐扫,將then(7)push進(jìn)微任務(wù)

14,執(zhí)行微任務(wù)then(7)

15仗哨,輸出7

16形庭,取出setTimeout(2)

17,輸出2

18厌漂,將then(3)push進(jìn)微任務(wù)

19萨醒,執(zhí)行微任務(wù)then(3)

20,輸出3

21苇倡,在執(zhí)行棧底部添加nextTick(4)

22富纸,輸出4

23囤踩,取出setTimeout(6)

24,輸出6

最后結(jié)果是:1晓褪,10堵漱,8,9涣仿,5勤庐,7,2好港,3愉镰,4,6

參考

?著作權(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ō)我怎么就攤上這事赋兵”恃剩” “怎么了?”我有些...
    開封第一講書人閱讀 153,443評(píng)論 0 344
  • 文/不壞的土叔 我叫張陵霹期,是天一觀的道長(zhǎng)叶组。 經(jīng)常有香客問(wèn)我,道長(zhǎng)历造,這世上最難降的妖魔是什么甩十? 我笑而不...
    開封第一講書人閱讀 55,475評(píng)論 1 279
  • 正文 為了忘掉前任,我火速辦了婚禮吭产,結(jié)果婚禮上侣监,老公的妹妹穿的比我還像新娘。我一直安慰自己臣淤,他們只是感情好橄霉,可當(dāng)我...
    茶點(diǎn)故事閱讀 64,458評(píng)論 5 374
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著邑蒋,像睡著了一般姓蜂。 火紅的嫁衣襯著肌膚如雪按厘。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 49,185評(píng)論 1 284
  • 那天覆糟,我揣著相機(jī)與錄音刻剥,去河邊找鬼。 笑死滩字,一個(gè)胖子當(dāng)著我的面吹牛造虏,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播麦箍,決...
    沈念sama閱讀 38,451評(píng)論 3 401
  • 文/蒼蘭香墨 我猛地睜開眼漓藕,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼!你這毒婦竟也來(lái)了挟裂?” 一聲冷哼從身側(cè)響起享钞,我...
    開封第一講書人閱讀 37,112評(píng)論 0 261
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤,失蹤者是張志新(化名)和其女友劉穎诀蓉,沒想到半個(gè)月后栗竖,有當(dāng)?shù)厝嗽跇淞掷锇l(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
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望锡移。 院中可真熱鬧,春花似錦漆际、人聲如沸淆珊。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,357評(píng)論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)施符。三九已至往声,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間戳吝,已是汗流浹背浩销。 一陣腳步聲響...
    開封第一講書人閱讀 31,590評(píng)論 1 261
  • 我被黑心中介騙來(lái)泰國(guó)打工, 沒想到剛下飛機(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)容