日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當(dāng)前位置: 首頁 > 编程资源 > 编程问答 >内容正文

编程问答

js事件循环

發(fā)布時(shí)間:2023/12/13 编程问答 23 豆豆
生活随笔 收集整理的這篇文章主要介紹了 js事件循环 小編覺得挺不錯(cuò)的,現(xiàn)在分享給大家,幫大家做個(gè)參考.

之前有看過一些事件循環(huán)的博客,不過一陣子沒看就發(fā)現(xiàn)自己忘光了,所以決定來自己寫一個(gè)博客總結(jié)下!

首先,我們來解釋下事件循環(huán)是個(gè)什么東西:

就我們所知,瀏覽器的js是單線程的,也就是說,在同一時(shí)刻,最多也只有一個(gè)代碼段在執(zhí)行,可是瀏覽器又能很好的處理異步請(qǐng)求,那么到底是為什么呢?我們先來看一張圖(這張圖來自于http://www.zcfy.cc/article/node-js-at-scale-understanding-the-node-js-event-loop-risingstack-1652.html)

從上圖我們可以看出,js主線程它是有一個(gè)執(zhí)行棧的,所有的js代碼都會(huì)在執(zhí)行棧里運(yùn)行。在執(zhí)行代碼過程中,如果遇到一些異步代碼(比如setTimeout,ajax,promise.then以及用戶點(diǎn)擊等操作),那么瀏覽器就會(huì)將這些代碼放到一個(gè)線程(在這里我們叫做幕后線程)中去等待,不阻塞主線程的執(zhí)行,主線程繼續(xù)執(zhí)行棧中剩余的代碼,當(dāng)幕后線程(background thread)里的代碼準(zhǔn)備好了(比如setTimeout時(shí)間到了,ajax請(qǐng)求得到響應(yīng)),該線程就會(huì)將它的回調(diào)函數(shù)放到任務(wù)隊(duì)列中等待執(zhí)行。而當(dāng)主線程執(zhí)行完棧中的所有代碼后,它就會(huì)檢查任務(wù)隊(duì)列是否有任務(wù)要執(zhí)行,如果有任務(wù)要執(zhí)行的話,那么就將該任務(wù)放到執(zhí)行棧中執(zhí)行。如果當(dāng)前任務(wù)隊(duì)列為空的話,它就會(huì)一直循環(huán)等待任務(wù)到來。因此,這叫做事件循環(huán)。

那么,問題來了。如果任務(wù)隊(duì)列中,有很多個(gè)任務(wù)的話,那么要先執(zhí)行哪一個(gè)任務(wù)呢?

其實(shí)(正如上圖所示),js是有兩個(gè)任務(wù)隊(duì)列的,一個(gè)叫做Macrotask Queue(Task Queue),一個(gè)叫做Microtask Queue

  • 前者主要是進(jìn)行一些比較大型的工作,常見的有setTimeout,setInterval,用戶交互操作,UI渲染等
  • 后者主要是進(jìn)行一些比較小型的工作,常見的有Promise,process.nextTick(nodejs)

那么,兩者有什么具體的區(qū)別呢?或者說,如果兩種任務(wù)同時(shí)出現(xiàn)的話,應(yīng)該選擇哪一個(gè)呢?
其實(shí)事件循環(huán)做的事情如下:

  • 檢查Macrotask 隊(duì)列是否為空,若不為空,則進(jìn)行下一步,若為空,則跳到3
  • 從Macrotask隊(duì)列中取隊(duì)首(在隊(duì)列時(shí)間最長)的任務(wù)進(jìn)去執(zhí)行棧中執(zhí)行(僅僅一個(gè)),執(zhí)行完后進(jìn)入下一步
  • 檢查Microtask隊(duì)列是否為空,若不為空,則進(jìn)入下一步,否則,跳到1(開始新的事件循環(huán))
  • 從Microtask隊(duì)列中取隊(duì)首(在隊(duì)列時(shí)間最長)的任務(wù)進(jìn)去事件隊(duì)列執(zhí)行,執(zhí)行完后,跳到3
  • 其中,在執(zhí)行代碼過程中新增的microtask任務(wù)會(huì)在當(dāng)前事件循環(huán)周期內(nèi)執(zhí)行,而新增的macrotask任務(wù)只能等到下一個(gè)事件循環(huán)才能執(zhí)行了(一個(gè)事件循環(huán)只執(zhí)行一個(gè)macrotask)
    首先,我們先來看一段代碼

    console.log(1) setTimeout(function() {//settimeout1console.log(2) }, 0); const intervalId = setInterval(function() {//setinterval1console.log(3) }, 0) setTimeout(function() {//settimeout2console.log(10)new Promise(function(resolve) {//promise1console.log(11)resolve()}).then(function() {console.log(12)}).then(function() {console.log(13)clearInterval(intervalId)}) }, 0);//promise2 Promise.resolve().then(function() {console.log(7)}).then(function() {console.log(8)}) console.log(9)

    你覺得結(jié)果應(yīng)該是什么呢?
    我在node環(huán)境和chrome控制臺(tái)輸出的結(jié)果如下:

    1 9 7 8 2 3 10 11 12 13

    在上面的例子中
    第一次事件循環(huán):

  • console.log(1)被執(zhí)行,輸出1
  • settimeout1執(zhí)行,加入macrotask隊(duì)列
  • setinterval1執(zhí)行,加入macrotask隊(duì)列
  • settimeout2執(zhí)行,加入macrotask隊(duì)列
  • promise2執(zhí)行,它的兩個(gè)then函數(shù)加入microtask隊(duì)列
  • console.log(9)執(zhí)行,輸出9
  • 根據(jù)事件循環(huán)的定義,接下來會(huì)執(zhí)行新增的microtask任務(wù),按照進(jìn)入隊(duì)列的順序,執(zhí)行console.log(7)和console.log(8),輸出7和8
    microtask隊(duì)列為空,回到第一步,進(jìn)入下一個(gè)事件循環(huán),此時(shí)macrotask隊(duì)列為: settimeout1,setinterval1,settimeout2
  • 第二次事件循環(huán):

  • 從macrotask隊(duì)列里取位于隊(duì)首的任務(wù)(settimeout1)并執(zhí)行,輸出2
    microtask隊(duì)列為空,回到第一步,進(jìn)入下一個(gè)事件循環(huán),此時(shí)macrotask隊(duì)列為: setinterval1,settimeout2
  • 第三次事件循環(huán):

  • 從macrotask隊(duì)列里取位于隊(duì)首的任務(wù)(setinterval1)并執(zhí)行,輸出3,然后又將新生成的setinterval1加入macrotask隊(duì)列
    microtask隊(duì)列為空,回到第一步,進(jìn)入下一個(gè)事件循環(huán),此時(shí)macrotask隊(duì)列為: settimeout2,setinterval1
  • 第四次事件循環(huán):

  • 從macrotask隊(duì)列里取位于隊(duì)首的任務(wù)(settimeout2)并執(zhí)行,輸出10,并且執(zhí)行new Promise內(nèi)的函數(shù)(new Promise內(nèi)的函數(shù)是同步操作,并不是異步操作),輸出11,并且將它的兩個(gè)then函數(shù)加入microtask隊(duì)列
  • 從microtask隊(duì)列中,取隊(duì)首的任務(wù)執(zhí)行,直到為空為止。因此,兩個(gè)新增的microtask任務(wù)按順序執(zhí)行,輸出12和13,并且將setinterval1清空
    此時(shí),microtask隊(duì)列和macrotask隊(duì)列都為空,瀏覽器會(huì)一直檢查隊(duì)列是否為空,等待新的任務(wù)加入隊(duì)列。
  • 在這里,大家可以會(huì)想,在第一次循環(huán)中,為什么不是macrotask先執(zhí)行?因?yàn)榘凑樟鞒痰脑?#xff0c;不應(yīng)該是先檢查macrotask隊(duì)列是否為空,再檢查microtask隊(duì)列嗎?
    原因:因?yàn)橐婚_始js主線程中跑的任務(wù)就是macrotask任務(wù),而根據(jù)事件循環(huán)的流程,一次事件循環(huán)只會(huì)執(zhí)行一個(gè)macrotask任務(wù),因此,執(zhí)行完主線程的代碼后,它就去從microtask隊(duì)列里取隊(duì)首任務(wù)來執(zhí)行。

    注意:

    由于在執(zhí)行microtask任務(wù)的時(shí)候,只有當(dāng)microtask隊(duì)列為空的時(shí)候,它才會(huì)進(jìn)入下一個(gè)事件循環(huán),因此,如果它源源不斷地產(chǎn)生新的microtask任務(wù),就會(huì)導(dǎo)致主線程一直在執(zhí)行microtask任務(wù),而沒有辦法執(zhí)行macrotask任務(wù),這樣我們就無法進(jìn)行UI渲染/IO操作/ajax請(qǐng)求了,因此,我們應(yīng)該避免這種情況發(fā)生。在nodejs里的process.nexttick里,就可以設(shè)置最大的調(diào)用次數(shù),以此來防止阻塞主線程。

    以此,我們來引入一個(gè)新的問題,定時(shí)器的問題。定時(shí)器是否是真實(shí)可靠的呢?比如我執(zhí)行一個(gè)命令:setTimeout(task, 100),他是否就能準(zhǔn)確的在100毫秒后執(zhí)行呢?其實(shí)根據(jù)以上的討論,我們就可以得知,這是不可能的。
    原因我想大家應(yīng)該也都知道了,因?yàn)槟銏?zhí)行setTimeout(task,100)后,其實(shí)只是確保這個(gè)任務(wù),會(huì)在100毫秒后進(jìn)入macrotask隊(duì)列,但并不意味著他能立刻運(yùn)行,可能當(dāng)前主線程正在進(jìn)行一個(gè)耗時(shí)的操作,也可能目前microtask隊(duì)列有很多個(gè)任務(wù),所以這也可能是大家一直詬病setTimeout的原因吧哈哈哈哈

    以上,只是我個(gè)人對(duì)事件循環(huán)的一些看法, 以及借鑒了其他優(yōu)秀文章

    參考:
    http://www.zcfy.cc/article/node-js-at-scale-understanding-the-node-js-event-loop-risingstack-1652.html
    https://github.com/ccforward/cc/issues/47

    轉(zhuǎn)載于:https://www.cnblogs.com/chenjg/p/7000044.html

    總結(jié)

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

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