日韩av黄I国产麻豆传媒I国产91av视频在线观看I日韩一区二区三区在线看I美女国产在线I麻豆视频国产在线观看I成人黄色短片

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 >

php event loop,理解javascript中的事件循环(Event Loop)

發(fā)布時間:2024/1/23 58 豆豆
生活随笔 收集整理的這篇文章主要介紹了 php event loop,理解javascript中的事件循环(Event Loop) 小編覺得挺不錯的,現(xiàn)在分享給大家,幫大家做個參考.

背景

在研究js的異步的實現(xiàn)方式的時候,發(fā)現(xiàn)了JavaScript 中的 macrotask 和 microtask 的概念。在查閱了一番資料之后,對其中的執(zhí)行機制有所了解,下面整理出來,希望可以幫助更多人。

先了解一下js的任務(wù)執(zhí)行機制

首先,javascript是單線程的,所以只能通過異步解決性能問題(否則,如果前面一個任務(wù)阻塞了,那么后續(xù)的任務(wù)都要等待,這種效果是無法接受的)。js在執(zhí)行代碼時存在著兩個比較重要的東西:執(zhí)行棧和任務(wù)隊列,這兩個東西都是用來存儲任務(wù)的,區(qū)別在于:執(zhí)行棧里面存著的都是同步任務(wù),也就是要按順序執(zhí)行的任務(wù);而任務(wù)隊列中存著的是一些異步任務(wù),這些異步任務(wù)一定要等到執(zhí)行棧清空后才會執(zhí)行(這句話很重要)。關(guān)于任務(wù)隊列,它還分成兩種,一種叫作macrotask queue(姑且這么命名,因為嚴格來說規(guī)范中只有說task,并沒有提到macrotask這個概念。這里為了容易區(qū)分,可以理解為macrotask=task!=microtask),另一種叫作microtask queue。如果同時考慮node環(huán)境和瀏覽器環(huán)境的話,這兩種任務(wù)分別對應(yīng)以下api:

microtasks:

process.nextTick

promise

Object.observe

MutationObserver

macrotasks:

setTimeout

setInterval

setImmediate

I/O

UI渲染

script標簽中的整體代碼

javascript在執(zhí)行時,先從 macrotasks 隊列開始執(zhí)行,取出第一個 macrotask 放入執(zhí)行棧執(zhí)行,在執(zhí)行過程中,如果遇到 macrotask,則將該 macrotask 放入 macrotask 隊列,繼續(xù)運行執(zhí)行棧中的后續(xù)代碼。如果遇到microtask,那么將該microtask放入microtask隊列,繼續(xù)向下運行執(zhí)行棧中的后續(xù)代碼。當執(zhí)行棧中的代碼全部執(zhí)行完成后,從microtasks隊列中取出所有的microtask放入執(zhí)行棧執(zhí)行。執(zhí)行完畢后,再從macrotasks 隊列取出下一個macrotask放入執(zhí)行棧。然后不斷重復(fù)上述流程。這一過程也被稱作事件循環(huán)(Event Loop)。

javascript就是通過這種機制來實現(xiàn)異步的。主線程會暫時存儲I/O等異步操作,直接向下執(zhí)行,當某個異步事件觸發(fā)時,再通知主線程執(zhí)行相應(yīng)的回調(diào)函數(shù),通過這種機制,javascript避免了單線程中異步操作耗時對后續(xù)任務(wù)的影響。

圖解事件循環(huán)流程

根據(jù)圖中描述,一次事件循環(huán)的執(zhí)行步驟如下:

1、從macrotask queue中取出最早的任務(wù)

2、在執(zhí)行棧中執(zhí)行第一步取出的任務(wù)

如果任務(wù)中存在microtask,將其壓入到microtask queue中

如果任務(wù)中存在macrotask,將其壓入到macrotask queue中

直到執(zhí)行完畢

3、執(zhí)行棧設(shè)置為null

4、從macrotask queue中刪除執(zhí)行過的macrotask

5、取出microtask queue中的全部任務(wù),放入執(zhí)行棧,

如果任務(wù)中存在microtask,將其壓入到microtask queue中

如果任務(wù)中存在macrotask,將其壓入到macrotask queue中

注意:這里產(chǎn)生的microtask(也就是microtask產(chǎn)生的microtask )也會在這一步驟中執(zhí)行。

直到當前microtask queue為空,此步驟結(jié)束。

6、執(zhí)行第一步的操作

實例驗證

我們執(zhí)行如下一段代碼,用上面的思路執(zhí)行,看一下結(jié)果是否和預(yù)期的一致。

console.log('start')

const interval = setInterval(() => {

console.log('setInterval')

}, 0)

setTimeout(() => {

console.log('setTimeout 1')

Promise.resolve()

.then(() => {

console.log('promise 3')

})

.then(() => {

console.log('promise 4')

})

.then(() => {

setTimeout(() => {

console.log('setTimeout 2')

Promise.resolve()

.then(() => {

console.log('promise 5')

})

.then(() => {

console.log('promise 6')

})

.then(() => {

clearInterval(interval)

})

}, 0)

})

}, 0)

Promise.resolve()

.then(() => {

console.log('promise 1')

})

.then(() => {

console.log('promise 2')

})

按照上面的思路,我們來理一下,預(yù)測一下執(zhí)行結(jié)果,看看實際效果是否是這樣的。

執(zhí)行流程:

第一輪:

1、首先這一整段js代碼作為一個macrotask先被執(zhí)行

2、遇到console.log('start'),輸出start

3、遇到setInterval,回調(diào)函數(shù)作為macrotask壓入到macrotask queue中,

此時macrotask queue:[setInterval]

4、遇到setTimeout,回調(diào)函數(shù)作為macrotask壓入到macrotask queue中,

此時macrotask queue:[setInterval,setTimeout1]

5、遇到Promise,并且調(diào)用了resolve方法,觸發(fā)了回調(diào),回調(diào)作為microtask壓入到microtask queue中

此時microtask queue:[promise 1,promise 2]

6、執(zhí)行棧為空,將microtask queue中的任務(wù)放入執(zhí)行棧

7、執(zhí)行microtask queue中Promise的回調(diào)任務(wù),分別打印promise 1,promise 2

8、執(zhí)行棧為空,microtask queue為空,開始下一輪事件循環(huán)

目前的console中打印內(nèi)容:

start

promise 1

promise 2

目前macrotask queue:[setInterval,setTimeout1]

第二輪:

1、從macrotask queue中取出最早的任務(wù),這里對應(yīng)的是第一輪中第3步的回調(diào)函數(shù):console.log('setInterval'),輸出setInterval

2、setInterval的回調(diào)函數(shù)作為macrotask壓入到macrotask queue中

此時macrotask queue:[setTimeout1,setInterval]

3、執(zhí)行棧為空,microtask queue為空,開始下一輪事件循環(huán)

目前的console中打印內(nèi)容:

start

promise 1

promise 2

setInterval

目前macrotask queue:[setTimeout1,setInterval]

第三輪:

1、從macrotask queue中取出最早的任務(wù),目前是setTimeout1的回調(diào),將取出的任務(wù)放入執(zhí)行棧執(zhí)行

2、遇到console.log('setTimeout 1'),輸出setTimeout 1

3、遇到Promise,并且調(diào)用了resolve方法,觸發(fā)回調(diào),回調(diào)作為microtask壓入到microtask queue中

此時microtask queue:[promise 3,promise 4,() => {setTimeout 2}]

4、執(zhí)行棧為空,將microtask queue中的任務(wù)放入執(zhí)行棧

5、執(zhí)行microtask queue中Promise的回調(diào)任務(wù):

輸出promise 3

輸出promise 4

將setTimeout 2壓入macrotask queue

6、執(zhí)行棧為空,microtask queue為空,開始下一輪事件循環(huán)

目前的console中打印內(nèi)容:

start

promise 1

promise 2

setInterval

setTimeout 1

promise 3

promise 4

目前macrotask queue:[setInterval,setTimeout2]

第四輪:

1、從macrotask queue中取出最早的任務(wù),這里對應(yīng)的是setInterval,輸出setInterval

2、setInterval的回調(diào)函數(shù)作為macrotask壓入到macrotask queue中

此時macrotask queue:[setTimeout2,setInterval]

3、執(zhí)行棧為空,microtask queue為空,開始下一輪事件循環(huán)

目前的console中打印內(nèi)容:

start

promise 1

promise 2

setInterval

setTimeout 1

promise 3

promise 4

setInterval

目前macrotask queue:[setTimeout2,setInterval]

第五輪:

1、從macrotask queue中取出最早的任務(wù),目前是setTimeout2的回調(diào),將取出的任務(wù)放入執(zhí)行棧執(zhí)行

2、遇到console.log('setTimeout 2')輸出setTimeout 2

3、遇到Promise,并且調(diào)用了resolve方法,觸發(fā)回調(diào),回調(diào)作為microtask壓入到microtask queue中

此時microtask queue:[promise 5,promise 6,() => {clearInterval}]

4、執(zhí)行棧為空,將microtask queue中的任務(wù)放入執(zhí)行棧

5、執(zhí)行microtask queue中Promise的回調(diào)任務(wù):

輸出promise 5

輸出promise 6

clearInterval清空setInterval計時器

6、執(zhí)行棧為空,microtask queue為空,macrotask queue為空,任務(wù)結(jié)束。

最終的console中打印內(nèi)容:

start

promise 1

promise 2

setInterval

setTimeout 1

promise 3

promise 4

setInterval

setTimeout 2

promise 5

promise 6

通過圖片可以看到,結(jié)果跟我們的預(yù)期一致,在promise2的后面作為方法的返回值,多打印了一個undefined,這個應(yīng)該好理解的。

這里面有個小問題,就是在不同的環(huán)境下(node/瀏覽器),promise4后面的setInterval表現(xiàn)可能會有差異,這里可能跟setTimeout和setInterval的最小間隔有關(guān),雖然我們寫成0ms,但實際上這個最小值是有限制的,現(xiàn)階段不同組織和不同的js引擎實現(xiàn)機制存在差異,不過這個問題不在本次討論范圍之內(nèi)了。如果我們將上述代碼中setInterval的間隔設(shè)置為10,那么整個執(zhí)行流程將嚴格符合我們的預(yù)期。

有什么用?

后續(xù)我們在代碼中使用Promise,setTimeout時,思路將更加清晰,用起來更佳得心應(yīng)手。

在閱讀一些源碼時,對于一些setTimeout相關(guān)的騷操作可以理解的更加深入。

理解javascript中的任務(wù)執(zhí)行流程,加深對異步流程的理解,少犯錯誤。

總結(jié)

js事件循環(huán)總是從一個macrotask開始執(zhí)行

一個事件循環(huán)過程中,只執(zhí)行一個macrotask,但是可能執(zhí)行多個microtask

執(zhí)行棧中的任務(wù)產(chǎn)生的microtask會在當前事件循環(huán)內(nèi)執(zhí)行

執(zhí)行棧中的任務(wù)產(chǎn)生的macrotask要在下一次事件循環(huán)才會執(zhí)行

總結(jié)

以上是生活随笔為你收集整理的php event loop,理解javascript中的事件循环(Event Loop)的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網(wǎng)站內(nèi)容還不錯,歡迎將生活随笔推薦給好友。