偷偷摘套内射激情视频,久久精品99国产国产精,中文字幕无线乱码人妻,中文在线中文a,性爽19p

瀏覽器和 Node.js 的 EventLoop 為什么這么設(shè)計(jì)?

開發(fā) 前端
avaScript 是用于實(shí)現(xiàn)網(wǎng)頁交互邏輯的,涉及到 dom 操作,如果多個(gè)線程同時(shí)操作需要做同步互斥的處理,為了簡(jiǎn)化就設(shè)計(jì)成了單線程,但是如果單線程的話,遇到定時(shí)邏輯、網(wǎng)絡(luò)請(qǐng)求又會(huì)阻塞住。怎么辦呢?

Event Loop 是 JavaScript 的基礎(chǔ)概念,面試必問,平時(shí)也經(jīng)常談到,但是有沒有想過為什么會(huì)有 Event Loop,它為什么會(huì)這樣設(shè)計(jì)的呢?

今天我們就來探索下原因。

瀏覽器的 Event Loop

JavaScript 是用于實(shí)現(xiàn)網(wǎng)頁交互邏輯的,涉及到 dom 操作,如果多個(gè)線程同時(shí)操作需要做同步互斥的處理,為了簡(jiǎn)化就設(shè)計(jì)成了單線程,但是如果單線程的話,遇到定時(shí)邏輯、網(wǎng)絡(luò)請(qǐng)求又會(huì)阻塞住。怎么辦呢?

可以加一層調(diào)度邏輯。把 JS 代碼封裝成一個(gè)個(gè)的任務(wù),放在一個(gè)任務(wù)隊(duì)列中,主線程就不斷的取任務(wù)執(zhí)行就好了。

每次取任務(wù)執(zhí)行,都會(huì)創(chuàng)建新的調(diào)用棧。

其中,定時(shí)器、網(wǎng)絡(luò)請(qǐng)求其實(shí)都是在別的線程執(zhí)行的,執(zhí)行完了之后在任務(wù)隊(duì)列里放個(gè)任務(wù),告訴主線程可以繼續(xù)往下執(zhí)行了。

因?yàn)檫@些異步任務(wù)是在別的線程執(zhí)行完,然后通過任務(wù)隊(duì)列通知下主線程,是一種事件機(jī)制,所以這個(gè)循環(huán)叫做 Event Loop。

這些在其他線程執(zhí)行的異步任務(wù)包括定時(shí)器(setTimeout、setInterval),UI 渲染、網(wǎng)絡(luò)請(qǐng)求(XHR 或 fetch)。

但是,現(xiàn)在的 Event Loop 有個(gè)嚴(yán)重的問題,沒有優(yōu)先級(jí)的概念,只是按照先后順序來執(zhí)行,那如果有高優(yōu)先級(jí)的任務(wù)就得不到及時(shí)的執(zhí)行了。所以,得設(shè)計(jì)一套插隊(duì)機(jī)制。

那就搞一個(gè)高優(yōu)先級(jí)的任務(wù)隊(duì)列就好了,每執(zhí)行完一個(gè)普通任務(wù),都去把所有高優(yōu)先級(jí)的任務(wù)給執(zhí)行完,之后再去執(zhí)行普通任務(wù)。

有了插隊(duì)機(jī)制之后,高優(yōu)任務(wù)就能得到及時(shí)的執(zhí)行。

這就是現(xiàn)在瀏覽器的 Event Loop。

其中普通任務(wù)叫做 MacroTask(宏任務(wù)),高優(yōu)任務(wù)叫做 MicroTask(微任務(wù))。

宏任務(wù)包括:setTimeout、setInterval、requestAnimationFrame、Ajax、fetch、script 標(biāo)簽的代碼。

微任務(wù)包括:Promise.then、MutationObserver、Object.observe。

怎么理解宏微任務(wù)的劃分呢?

定時(shí)器、網(wǎng)絡(luò)請(qǐng)求這種都是在別的線程跑完之后通知主線程的普通異步邏輯,所以都是宏任務(wù)。

而高優(yōu)任務(wù)的這三種也很好理解,MutationObserver 和 Object.observe 都是監(jiān)聽某個(gè)對(duì)象的變化的,變化是很瞬時(shí)的事情,肯定要馬上響應(yīng),不然可能又變了,Promise 是組織異步流程的,異步結(jié)束調(diào)用 then 也是很高優(yōu)的。

這就是瀏覽器里的 Event Loop 的設(shè)計(jì):設(shè)計(jì) Loop 機(jī)制和 Task 隊(duì)列是為了支持異步,解決邏輯執(zhí)行阻塞主線程的問題,設(shè)計(jì) MicroTask 隊(duì)列的插隊(duì)機(jī)制是為了解決高優(yōu)任務(wù)盡早執(zhí)行的問題。

但是后來,JS 的執(zhí)行環(huán)境不只是瀏覽器一種了,還有了 Node.js,它同樣也要解決這些問題,但是它設(shè)計(jì)出來的 Event Loop 更細(xì)致一些。

Node.js 的 Event loop

Node.js 是一個(gè)新的 JS 運(yùn)行環(huán)境,它同樣要支持異步邏輯,包括定時(shí)器、IO、網(wǎng)絡(luò)請(qǐng)求,很明顯,也可以用 Event Loop 那一套來跑。

但是呢,瀏覽器那套 Event Loop 就是為瀏覽器設(shè)計(jì)的,對(duì)于做高性能服務(wù)器來說,那種設(shè)計(jì)還是有點(diǎn)粗糙了。

哪里粗糙呢?

瀏覽器的 Event Loop 只分了兩層優(yōu)先級(jí),一層是宏任務(wù),一層是微任務(wù)。但是宏任務(wù)之間沒有再劃分優(yōu)先級(jí),微任務(wù)之間也沒有再劃分優(yōu)先級(jí)。

而 Node.js 任務(wù)宏任務(wù)之間也是有優(yōu)先級(jí)的,比如定時(shí)器 Timer 的邏輯就比 IO 的邏輯優(yōu)先級(jí)高,因?yàn)樯婕暗綍r(shí)間,越早越準(zhǔn)確;而 close 資源的處理邏輯優(yōu)先級(jí)就很低,因?yàn)椴?close 最多多占點(diǎn)內(nèi)存等資源,影響不大。

于是就把宏任務(wù)隊(duì)列拆成了五個(gè)優(yōu)先級(jí):Timers、Pending、Poll、Check、Close。

解釋一下這五種宏任務(wù):

Timers Callback:涉及到時(shí)間,肯定越早執(zhí)行越準(zhǔn)確,所以這個(gè)優(yōu)先級(jí)最高很容易理解。

Pending Callback:處理網(wǎng)絡(luò)、IO 等異常時(shí)的回調(diào),有的 *niux 系統(tǒng)會(huì)等待發(fā)生錯(cuò)誤的上報(bào),所以得處理下。

Poll Callback:處理 IO 的 data,網(wǎng)絡(luò)的 connection,服務(wù)器主要處理的就是這個(gè)。

Check Callback:執(zhí)行 setImmediate 的回調(diào),特點(diǎn)是剛執(zhí)行完 IO 之后就能回調(diào)這個(gè)。

Close Callback:關(guān)閉資源的回調(diào),晚點(diǎn)執(zhí)行影響也不到,優(yōu)先級(jí)最低。

所以呢,Node.js 的 Event Loop 就是這樣跑的了:

還有一點(diǎn)不同要特別注意:

Node.js 的 Event Loop 并不是瀏覽器那種一次執(zhí)行一個(gè)宏任務(wù),然后執(zhí)行所有的微任務(wù),而是執(zhí)行完一定數(shù)量的 Timers 宏任務(wù),再去執(zhí)行所有微任務(wù),然后再執(zhí)行一定數(shù)量的 Pending 的宏任務(wù),然后再去執(zhí)行所有微任務(wù),剩余的 Poll、Check、Close 的宏任務(wù)也是這樣。

為什么這樣呢?

其實(shí)按照優(yōu)先級(jí)來看很容易理解:

假設(shè)瀏覽器里面的宏任務(wù)優(yōu)先級(jí)是 1,所以是按照先后順序依次執(zhí)行,也就是一個(gè)宏任務(wù),所有的微任務(wù),再一個(gè)宏任務(wù),再所有的微任務(wù)。

而 Node.js 的 宏任務(wù)之間也是有優(yōu)先級(jí)的,所以 Node.js 的 Event Loop 每次都是把當(dāng)前優(yōu)先級(jí)的所有宏任務(wù)跑完再去跑微任務(wù),然后再跑下一個(gè)優(yōu)先級(jí)的宏任務(wù)。

也就是是一定數(shù)量的 Timers 宏任務(wù),再所有微任務(wù),再一定數(shù)量的 Pending Callback 宏任務(wù),再所有微任務(wù)這樣。

為什么說是一定數(shù)量呢?

因?yàn)槿绻硞€(gè)階段宏任務(wù)太多,下個(gè)階段就一直執(zhí)行不到了,所以有個(gè)上限的限制,剩余的下個(gè) Event Loop 再繼續(xù)執(zhí)行。

除了宏任務(wù)有優(yōu)先級(jí),微任務(wù)也劃分了優(yōu)先級(jí),多了一個(gè) process.nextTick 的高優(yōu)先級(jí)微任務(wù),在所有的普通微任務(wù)之前來跑。

所以,Node.js 的 Event Loop 的完整流程就是這樣的:

  • Timers 階段:執(zhí)行一定數(shù)量的定時(shí)器,也就是 setTimeout、setInterval 的 callback,太多的話留到下次執(zhí)行
  • 微任務(wù):執(zhí)行所有 nextTick 的微任務(wù),再執(zhí)行其他的普通微任務(wù)
  • Pending 階段:執(zhí)行一定數(shù)量的 IO 和網(wǎng)絡(luò)的異?;卣{(diào),太多的話留到下次執(zhí)行
  • 微任務(wù):執(zhí)行所有 nextTick 的微任務(wù),再執(zhí)行其他的普通微任務(wù)
  • Idle/Prepare 階段:內(nèi)部用的一個(gè)階段
  • 微任務(wù):執(zhí)行所有 nextTick 的微任務(wù),再執(zhí)行其他的普通微任務(wù)
  • Poll 階段:執(zhí)行一定數(shù)量的文件的 data 回調(diào)、網(wǎng)絡(luò)的 connection 回調(diào),太多的話留到下次執(zhí)行。如果沒有 IO 回調(diào)并且也沒有 timers、check 階段的回調(diào)要處理,就阻塞在這里等待 IO 事件
  • 微任務(wù):執(zhí)行所有 nextTick 的微任務(wù),再執(zhí)行其他的普通微任務(wù)
  • Check 階段:執(zhí)行一定數(shù)量的 setImmediate 的 callback,太多的話留到下次執(zhí)行。
  • 微任務(wù):執(zhí)行所有 nextTick 的微任務(wù),再執(zhí)行其他的普通微任務(wù)
  • Close 階段:執(zhí)行一定數(shù)量的 close 事件的 callback,太多的話留到下次執(zhí)行。
  • 微任務(wù):執(zhí)行所有 nextTick 的微任務(wù),再執(zhí)行其他的普通微任務(wù)

比起瀏覽器里的 Event Loop,明顯復(fù)雜了很多,但是經(jīng)過我們之前的分析,也能夠理解:

Node.js 對(duì)宏任務(wù)做了優(yōu)先級(jí)劃分,從高到低分別是 Timers、Pending、Poll、Check、Close 這 5 種,也對(duì)微任務(wù)做了劃分,也就是 nextTick 的微任務(wù)和其他微任務(wù)。執(zhí)行流程是先執(zhí)行完當(dāng)前優(yōu)先級(jí)的一定數(shù)量的宏任務(wù)(剩下的留到下次循環(huán)),然后執(zhí)行 process.nextTick 的微任務(wù),再執(zhí)行普通微任務(wù),之后再執(zhí)行下個(gè)優(yōu)先級(jí)的一定數(shù)量的宏任務(wù)。。這樣不斷循環(huán)。其中還有一個(gè) Idle/Prepare 階段是給 Node.js 內(nèi)部邏輯用的,不需要關(guān)心。

改變了瀏覽器 Event Loop 里那種一次執(zhí)行一個(gè)宏任務(wù)的方式,可以讓高優(yōu)先級(jí)的宏任務(wù)更早的得到執(zhí)行,但是也設(shè)置了個(gè)上限,避免下個(gè)階段一直得不到執(zhí)行。

還有一個(gè)特別要注意的點(diǎn),就是 poll 階段:如果執(zhí)行到 poll 階段,發(fā)現(xiàn) poll 隊(duì)列為空并且 timers 隊(duì)列、check 隊(duì)列都沒有任務(wù)要執(zhí)行,那么就阻塞的等在這里等 IO 事件,而不是空轉(zhuǎn)。 這點(diǎn)設(shè)計(jì)也是因?yàn)榉?wù)器主要是處理 IO 的,阻塞在這里可以更早的響應(yīng) IO。

完整的 Node.js 的 Event Loop 是這樣的:

對(duì)比下瀏覽器的 Event Loop:

兩個(gè) JS 運(yùn)行環(huán)境的 Event Loop 整體設(shè)計(jì)思路是差不多的,只不過 Node.js 的 Event Loop 對(duì)宏任務(wù)和微任務(wù)做了更細(xì)粒度的劃分,也很容易理解,畢竟 Node.js 面向的環(huán)境和瀏覽器不同,更重要的是服務(wù)端對(duì)性能的要求會(huì)更高。

總結(jié)

JavaScript 最早是用于寫網(wǎng)頁交互邏輯的,為了避免多線程同時(shí)修改 dom 的同步問題,設(shè)計(jì)成了單線程,又為了解決單線程的阻塞問題,加了一層調(diào)度邏輯,也就是 Loop 循環(huán)和 Task 隊(duì)列,把阻塞的邏輯放到其他線程跑,從而支持了異步。然后為了支持高優(yōu)先級(jí)的任務(wù)調(diào)度,又引入了微任務(wù)隊(duì)列,這就是瀏覽器的 Event Loop 機(jī)制:每次執(zhí)行一個(gè)宏任務(wù),然后執(zhí)行所有微任務(wù)。

Node.js 也是一個(gè) JS 運(yùn)行環(huán)境,想支持異步同樣要用 Event Loop,只不過服務(wù)端環(huán)境更復(fù)雜,對(duì)性能要求更高,所以 Node.js 對(duì)宏微任務(wù)都做了更細(xì)粒度的優(yōu)先級(jí)劃分:

Node.js 里劃分了 5 種宏任務(wù),分別是 Timers、Pending、Poll、Check、Close。又劃分了 2 種微任務(wù),分別是 process.nextTick 的微任務(wù)和其他的微任務(wù)。

Node.js 的 Event Loop 流程是執(zhí)行當(dāng)前階段的一定數(shù)量的宏任務(wù)(剩余的到下個(gè)循環(huán)執(zhí)行),然后執(zhí)行所有微任務(wù),一共有 Timers、Pending、Idle/Prepare、Poll、Check、Close 6 個(gè)階段。

其中 Idle/Prepare 階段是 Node.js 內(nèi)部用的,不用關(guān)心。

特別要注意的是 Poll 階段,如果執(zhí)行到這里,poll 隊(duì)列為空并且 timers、check 隊(duì)列也為空,就一直阻塞在這里等待 IO,直到 timers、check 隊(duì)列有回調(diào)再繼續(xù) loop 。

Event Loop 是 JS 為了支持異步和任務(wù)優(yōu)先級(jí)而設(shè)計(jì)的一套調(diào)度邏輯,針對(duì)瀏覽器、Node.js 等不同環(huán)境有不同的設(shè)計(jì)(主要是任務(wù)優(yōu)先級(jí)的劃分粒度不同),Node.js 面對(duì)的環(huán)境更復(fù)雜、對(duì)性能要求更高,所以 Event Loop 設(shè)計(jì)的更復(fù)雜一些。

 

責(zé)任編輯:武曉燕 來源: 神光的編程秘籍
相關(guān)推薦

2021-12-08 07:55:41

EventLoop瀏覽器事件

2012-03-09 09:11:29

Node.js

2022-12-22 07:44:04

2021-09-03 13:42:54

Node.js異步性能

2012-05-02 15:56:20

PHP

2020-09-15 08:26:25

瀏覽器緩存

2018-04-22 00:01:43

JavaScript Node 語言

2011-09-02 14:47:48

Node

2016-05-18 10:15:25

PythonNode.js

2023-01-10 14:11:26

2011-12-23 13:58:57

node.js

2023-09-08 14:12:04

2012-06-04 10:35:55

FirefoxChrome瀏覽器

2011-02-22 09:50:21

2020-10-20 09:12:57

axios核心原理

2022-06-13 21:52:02

CDN網(wǎng)絡(luò)節(jié)點(diǎn)

2022-07-20 11:36:47

瀏覽器代碼

2013-11-01 09:34:56

Node.js技術(shù)

2021-09-26 05:04:45

瀏覽器AppActivity

2015-03-10 10:59:18

Node.js開發(fā)指南基礎(chǔ)介紹
點(diǎn)贊
收藏

51CTO技術(shù)棧公眾號(hào)