成人国产在线小视频_日韩寡妇人妻调教在线播放_色成人www永久在线观看_2018国产精品久久_亚洲欧美高清在线30p_亚洲少妇综合一区_黄色在线播放国产_亚洲另类技巧小说校园_国产主播xx日韩_a级毛片在线免费

資訊專欄INFORMATION COLUMN

理解javascript中的事件循環(huán)(Event Loop)

mykurisu / 3428人閱讀

摘要:主線程會暫時存儲等異步操作,直接向下執(zhí)行,當某個異步事件觸發(fā)時,再通知主線程執(zhí)行相應的回調(diào)函數(shù),通過這種機制,避免了單線程中異步操作耗時對后續(xù)任務的影響。

背景

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

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

首先,javascript是單線程的,所以只能通過異步解決性能問題(否則,如果前面一個任務阻塞了,那么后續(xù)的任務都要等待,這種效果是無法接受的)。js在執(zhí)行代碼時存在著兩個比較重要的東西:執(zhí)行棧和任務隊列,這兩個東西都是用來存儲任務的,區(qū)別在于:執(zhí)行棧里面存著的都是同步任務,也就是要按順序執(zhí)行的任務;而任務隊列中存著的是一些異步任務,這些異步任務一定要等到執(zhí)行棧清空后才會執(zhí)行(這句話很重要)。關于任務隊列,它還分成兩種,一種叫作macrotask queue(姑且這么命名,因為嚴格來說規(guī)范中只有說task,并沒有提到macrotask這個概念。這里為了容易區(qū)分,可以理解為macrotask=task!=microtask),另一種叫作microtask queue。如果同時考慮node環(huán)境和瀏覽器環(huán)境的話,這兩種任務分別對應以下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í)行棧。然后不斷重復上述流程。這一過程也被稱作事件循環(huán)(Event Loop)。
javascript就是通過這種機制來實現(xiàn)異步的。主線程會暫時存儲I/O等異步操作,直接向下執(zhí)行,當某個異步事件觸發(fā)時,再通知主線程執(zhí)行相應的回調(diào)函數(shù),通過這種機制,javascript避免了單線程中異步操作耗時對后續(xù)任務的影響。

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

根據(jù)圖中描述,一次事件循環(huán)的執(zhí)行步驟如下:
1、從macrotask queue中取出最早的任務
2、在執(zhí)行棧中執(zhí)行第一步取出的任務
如果任務中存在microtask,將其壓入到microtask queue中
如果任務中存在macrotask,將其壓入到macrotask queue中
直到執(zhí)行完畢
3、執(zhí)行棧設置為null
4、從macrotask queue中刪除執(zhí)行過的macrotask
5、取出microtask queue中的全部任務,放入執(zhí)行棧,
如果任務中存在microtask,將其壓入到microtask queue中
如果任務中存在macrotask,將其壓入到macrotask queue中
注意:這里產(chǎn)生的microtask(也就是microtask產(chǎn)生的microtask )也會在這一步驟中執(zhí)行。
直到當前microtask queue為空,此步驟結(jié)束。
6、執(zhí)行第一步的操作

實例驗證

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

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")
    })

按照上面的思路,我們來理一下,預測一下執(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中的任務放入執(zhí)行棧
7、執(zhí)行microtask queue中Promise的回調(diào)任務,分別打印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中取出最早的任務,這里對應的是第一輪中第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中取出最早的任務,目前是setTimeout1的回調(diào),將取出的任務放入執(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中的任務放入執(zhí)行棧
5、執(zhí)行microtask queue中Promise的回調(diào)任務:
輸出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中取出最早的任務,這里對應的是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中取出最早的任務,目前是setTimeout2的回調(diào),將取出的任務放入執(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中的任務放入執(zhí)行棧
5、執(zhí)行microtask queue中Promise的回調(diào)任務:
輸出promise 5
輸出promise 6
clearInterval清空setInterval計時器
6、執(zhí)行棧為空,microtask queue為空,macrotask queue為空,任務結(jié)束。
最終的console中打印內(nèi)容:
start
promise 1
promise 2
setInterval
setTimeout 1
promise 3
promise 4
setInterval
setTimeout 2
promise 5
promise 6

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

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

有什么用?

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

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

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

總結(jié)

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

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

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

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

文章版權歸作者所有,未經(jīng)允許請勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。

轉(zhuǎn)載請注明本文地址:http://systransis.cn/yun/107861.html

相關文章

  • 初窺JavaScript事件機制的實現(xiàn)(一)—— Node.js事件驅(qū)動實現(xiàn)概覽

    摘要:如果當前沒有事件也沒有定時器事件,則返回。相關資料關于的架構及設計思路的事件討論了使用線程池異步運行代碼。下一篇初窺事件機制的實現(xiàn)二中定時器的實現(xiàn) 在瀏覽器中,事件作為一個極為重要的機制,給予JavaScript響應用戶操作與DOM變化的能力;在Node.js中,事件驅(qū)動模型則是其高并發(fā)能力的基礎。 學習JavaScript也需要了解它的運行平臺,為了更好的理解JavaScript的事...

    lavor 評論0 收藏0
  • JavaScript Event loop 事件循環(huán)

    摘要:階段有兩個主要功能也會執(zhí)行時間定時器到達期望時間的回調(diào)函數(shù)執(zhí)行事件循環(huán)列表里的函數(shù)當進入階段并且沒有其余的定時器,那么如果事件循環(huán)列表不為空,則迭代同步的執(zhí)行隊列中的函數(shù)。如果沒有,則等待回調(diào)函數(shù)進入隊列并立即執(zhí)行。 Event Loop 本文以 Node.js 為例,講解 Event Loop 在 Node.js 的實現(xiàn),原文,JavaScript 中的實現(xiàn)大同小異。 什么是 Eve...

    luckyyulin 評論0 收藏0
  • FE.ES-理解Event Loop

    摘要:新加了一個微任務和一個宏任務在當前執(zhí)行棧的尾部下一次之前觸發(fā)回調(diào)函數(shù)。階段這個階段主要執(zhí)行一些系統(tǒng)操作帶來的回調(diào)函數(shù),如錯誤,如果嘗試鏈接時出現(xiàn)錯誤,一些會把這個錯誤報告給。 JavaScript引擎又稱為JavaScript解釋器,是JavaScript解釋為機器碼的工具,分別運行在瀏覽器和Node中。而根據(jù)上下文的不同,Event loop也有不同的實現(xiàn):其中Node使用了libu...

    longshengwang 評論0 收藏0
  • Javascript事件循環(huán)入門到“忘記”(一).md

    摘要:中叫做調(diào)用棧先進后出,后進先出。如下圖這是典型的內(nèi)存溢出,可能會出現(xiàn)在某些場景下需要遞歸,但業(yè)務邏輯中的判斷又沒能正常計算進入到預設情況,于是調(diào)用棧中不斷進入,又無法執(zhí)行完,就造成內(nèi)存溢出了。 本文主要介紹Javascript事件循環(huán)在瀏覽器上的一些特性和應用介紹。 Javascript小知識 JavaScript的并發(fā)模型基于事件循環(huán)(Event Loop)。這個模型與像C或者Jav...

    codeGoogle 評論0 收藏0
  • 淺析 JS 中的 EventLoop 事件循環(huán)(新手向)

    摘要:同時,如果執(zhí)行的過程中發(fā)現(xiàn)其他函數(shù),繼續(xù)入棧然后執(zhí)行。上面我們討論的其實都是同步代碼,代碼在運行的時候只用調(diào)用棧解釋就可以了。 序 Event Loop 這個概念相信大家或多或少都了解過,但是有一次被一個小伙伴問到它具體的原理的時候,感覺自己只知道個大概印象,于是計劃著寫一篇文章,用輸出倒逼輸入,讓自己重新學習這個概念,同時也能幫助更多的人理解它~ 概念 JavaScript 是一門 ...

    chadLi 評論0 收藏0
  • JavaScript執(zhí)行機制、事件循環(huán)

    摘要:曾經(jīng)的理解首先,是單線程語言,也就意味著同一個時間只能做一件事,那么為什么不是多線程呢這樣還能提高效率啊假定同時有兩個線程,一個線程在某個節(jié)點上編輯了內(nèi)容,而另一個線程刪除了這個節(jié)點,這時瀏覽器就很懵逼了,到底以執(zhí)行哪個操作呢所以,設計者把 Event Loop曾經(jīng)的理解 首先,JS是單線程語言,也就意味著同一個時間只能做一件事,那么 為什么JavaScript不是多線程呢?這樣還能提...

    rose 評論0 收藏0

發(fā)表評論

0條評論

最新活動
閱讀需要支付1元查看
<