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

資訊專欄INFORMATION COLUMN

理清瀏覽器下的事件循環(huán)機制(Event Loop)

nemo / 3057人閱讀

摘要:何為事件循環(huán)機制的任務(wù)分兩種,分別是同步任務(wù)和異步任務(wù)。如上圖所示主線程在執(zhí)行代碼的時候,遇到異步任務(wù)進入并注冊回調(diào)函數(shù),有了運行結(jié)果后將它添加到事件隊列中,然后繼續(xù)執(zhí)行下面的代碼,直到同步代碼執(zhí)行完。

我們知道,JavaScript作為瀏覽器的腳本語言,起初是為了與用戶交互和操作DOM,為了避免因為同時操作了同一DOM節(jié)點而引起沖突,被設(shè)計成為一種單線程語言。

而單線程語言最大的特性就是同一時間只能做一件事,這個任務(wù)未完成下一個任務(wù)就要等待,這樣無疑是對資源的極大浪費,而且嚴重時會引起阻塞,造成用戶體驗極差。這個時候就引出了異步的概念,而異步的核心就是事件循環(huán)機制Event Loop。

何為事件循環(huán)機制?

JavaScript的任務(wù)分兩種,分別是同步任務(wù)和異步任務(wù)。

同步任務(wù):在主線程上排隊執(zhí)行的任務(wù),只有前一個任務(wù)執(zhí)行完畢,才能執(zhí)行后一個任務(wù);

異步任務(wù):不進入主線程而進入"任務(wù)隊列"(task queue)的任務(wù),只有"任務(wù)隊列"通知主線程某個異步任務(wù)可以執(zhí)行了,該任務(wù)才會進入主線程執(zhí)行。

如上圖所示:

主線程在執(zhí)行代碼的時候,遇到異步任務(wù)進入Event Table并注冊回調(diào)函數(shù),有了運行結(jié)果后將它添加到事件隊列(callback queue)中,然后繼續(xù)執(zhí)行下面的代碼,直到同步代碼執(zhí)行完。

主線程執(zhí)行完同步代碼后,讀取callback queue中的任務(wù),如果有可執(zhí)行任務(wù)則進入主線程執(zhí)行

不斷重復以上步驟,就形成了事件循環(huán)(Event Loop)

結(jié)合上面步驟分析下這個例子:

1. 執(zhí)行主線程同步任務(wù),輸出start【1】,繼續(xù)往下執(zhí)行
2. 遇到setTimeout,進入event table注冊setTimeout回調(diào),setTimeout回調(diào)執(zhí)行完后,繼續(xù)往下執(zhí)行
3. 輸出end【2】,同步任務(wù)執(zhí)行完畢
4. 進入event queue,檢查是否有可執(zhí)行任務(wù),取出event queue中setTimeout任務(wù)開始執(zhí)行,輸出setTimeout【3】

結(jié)果依次為:start -> end -> setTimeout

瀏覽器環(huán)境下的異步任務(wù)
在瀏覽器和node中的事件循環(huán)與執(zhí)行機制是不同的,要注意區(qū)分,不要搞混。
執(zhí)行過程

瀏覽器環(huán)境的異步任務(wù)分為宏任務(wù)(macroTask)和微任務(wù)(microtask),當滿足條件時會分別被放進宏任務(wù)隊列和微任務(wù)隊列(先進先出),等待被執(zhí)行。

微任務(wù):
promise,MutationObserver

宏任務(wù):
script整體,setTimeout & setIntervat,I/O,UI render。

執(zhí)行過程如下:

如圖所示:

1. 把整體的script代碼作為宏任務(wù)執(zhí)行
2. 執(zhí)行過程中如果遇到宏任務(wù)和微任務(wù),滿足條件時分別添加至宏任務(wù)隊列和微任務(wù)隊列
3. 執(zhí)行完一個宏任務(wù)后,取出所有微任務(wù)依次執(zhí)行,如果微任務(wù)一直有新的被添加進來,則一直執(zhí)行,直到把微任務(wù)隊列清空
4. 不斷重復2和3,直到所有任務(wù)被清空,結(jié)束執(zhí)行。

分析:

第一輪:

輸出start【1】,將setTimeout回調(diào)函數(shù)@1,放進宏任務(wù)隊列;

將setTimeout回調(diào)函數(shù)@2,放進宏任務(wù)隊列;

將setTimeout回調(diào)函數(shù)@3,放進宏任務(wù)隊列;

執(zhí)行new Promise函數(shù)輸出promise4【2】,將Promise.then@1放進微任務(wù)隊列;

輸出end【3】,此時隊列如下所示:

第一輪宏任務(wù)執(zhí)行完畢,開始執(zhí)行微任務(wù),取出微任務(wù)Promise.then@1,輸出promise5【4】,此時微任務(wù)隊列被清空,開始第二輪執(zhí)行。

第二輪:

取出宏任務(wù)setTimeout回調(diào)函數(shù)@1,輸出timer1【5】,將回調(diào)函數(shù)中的Promise.then@2放進微任務(wù)隊列;

宏任務(wù)setTimeout回調(diào)函數(shù)@1中無宏任務(wù),開始執(zhí)行微任務(wù),取出Promise.then@2,輸出promise1【6】,此時:

setTimeout回調(diào)函數(shù)@1中宏任務(wù)隊列和微任務(wù)隊列均被清空,開始第三輪執(zhí)行

第三輪:

取出宏任務(wù)setTimeout回調(diào)函數(shù)@2,輸出timer2【7】,將Promise.then@3放進微任務(wù)隊列;

setTimeout回調(diào)函數(shù)@2中無宏任務(wù),開始執(zhí)行微任務(wù),取出Promise.then@3,輸出promise2【8】,此時:

宏任務(wù)setTimeout回調(diào)函數(shù)@2中宏任務(wù)隊列和微任務(wù)隊列均被清空,開始第四輪執(zhí)行

第四輪:

取出宏任務(wù)setTimeout回調(diào)函數(shù)@3,輸出timer3【9】,將Promise.then@4放進微任務(wù)隊列;

setTimeout回調(diào)函數(shù)@3中無宏任務(wù),開始執(zhí)行微任務(wù),取出Promise.then@4,輸出promise3【10】

現(xiàn)在宏任務(wù)對列和微任務(wù)隊列都被清空了,完成執(zhí)行,結(jié)果為:start > promise4 > end > promise5 > timer1 > promise1 > timer2 > promise2 > timer3 > promise3

引入 async/await

asnyc知識點傳送門

await表達式的運算結(jié)果取決于它右側(cè)的結(jié)果

當遇到await時,會阻塞函數(shù)體內(nèi)部處于await后面的代碼,跳出去執(zhí)行該函數(shù)外部的同步代碼,當外部同步代碼執(zhí)行完畢,再回到該函數(shù)內(nèi)部執(zhí)行剩余的代碼

補充aynsc的一點知識:

如果aynsc函數(shù)中return一個直接量,async 會把這個直接量通過Promise.resolve()封裝成Promise對象,如果什么都沒return,會被封裝成Promise.resolve(undefined)

那么 引入了async await之后的執(zhí)行過程是怎樣的呢?

分析:

第一輪:

執(zhí)行同步代碼,輸出:script start【1】,將setTimeout回調(diào)@1放入宏任務(wù)隊列;

進入aynsc1函數(shù)中,執(zhí)行同步代碼輸出:async1 start【2】,遇到await從右向左執(zhí)行,進入async2函數(shù),輸出:async2【3】;aynsc2函數(shù)體中未返回任何東西等價于返回了Promise.resolve(undefined),拿到返回值后進入aynsc1函數(shù)體中,繼續(xù)執(zhí)行剩下的部分,這時候aynsc1中注釋部分等價于:

async function async1() {
  console.log("async1 start");
  //await async2();
  //console.log("async1 end");
   await new Promise((resolve) => resolve()).then(resolve => {
     console.log("async1 end")
   })
}

將Promise.then@1推入到微任務(wù)隊列;

繼續(xù)執(zhí)行同步代碼,輸出:promise1【4】,將Promise.then@2推入微任務(wù)隊列

繼續(xù)執(zhí)行同步代碼,輸出:script end【5】,第一輪宏隊列任務(wù)執(zhí)行完畢,此時如下:

開始執(zhí)行微任務(wù),取出微任務(wù)Promise.then@1,值為undefined,這個時候Promise.then@1完成執(zhí)行,則await aynsc2()得到了值也完成了執(zhí)行,不再阻塞后面代碼,那么執(zhí)行同步代碼輸出:async1 end【6】;

取出微任務(wù)Promise.then@2,輸出:promise2【7】,微任務(wù)全部執(zhí)行完畢,現(xiàn)在開始第二輪執(zhí)行

第二輪:

取出宏任務(wù)隊列中的setTimeout@1,輸出setTimeout【8】

所有任務(wù)隊列均為空,結(jié)束執(zhí)行,輸出結(jié)果為:script start > async1 start > async2 > promise1 > script end > async1 end > promise2 > setTimeout

補充谷歌瀏覽器測試結(jié)果:

借用一個例子:await一個直接值的情況

分析:

第一輪:

執(zhí)行同步函數(shù),輸出:1【1】,進入async1函數(shù)中,輸出:2【2】,這個時候await雖然接收了一個直接值,但是還是要先執(zhí)行外邊的同步代碼之后才能執(zhí)行await后邊的值

繼續(xù)執(zhí)行同步代碼,輸出:3【3】,進入Promise函數(shù),輸出:4【4】,將Promise.then推入微任務(wù)隊列

同步代碼執(zhí)行完畢,進入 async1函數(shù)中輸出:5【5】

宏任務(wù)執(zhí)行完畢,進入微任務(wù)隊列,開始執(zhí)行微任務(wù);取出Promise.then,輸出:6【6】

任務(wù)隊列為空,執(zhí)行完畢,結(jié)果為: 1 > 2 > 3 > 4 > 5 > 6

再借個例子,這個有點復雜

分析:

第一輪:

將setTimeOut@1放入宏任務(wù)列隊;

執(zhí)行async1()函數(shù)體內(nèi)的函數(shù),輸出:1【1】,遇到await,進入aynsc2函數(shù)體,輸出:2【2】,將該函數(shù)體內(nèi)promise.then@1放入微任務(wù)隊列中;

執(zhí)行New promise .. 輸出3【3】,將該函數(shù)體內(nèi)Promise.then@2放入微任務(wù)隊列中,第一輪宏任務(wù)執(zhí)行完畢,此時:

開始執(zhí)行第一輪微任務(wù),取出Promise.then@1,輸出:4【4】,此時async2函數(shù)執(zhí)行完畢,進入aynsc1函數(shù),此時改動下aynsc1函數(shù),等價于:

async function async1() {
  console.log("1")
  //const data = await async2()
  //console.log("6")
   const data = await new Promise(resolve => resolve("async2的結(jié)果")).then((resolve) => {
                    console.log(6); 
                    return resolve;
                })

   return data;
}

將上面promise.then@3推入微任務(wù)隊列中,此時:

接著執(zhí)行微任務(wù),取出promise.then@2,輸出:5【5】,取出promise.then@3,輸出:6【6】,此時函數(shù)async1執(zhí)行完成,接著執(zhí)行async1().then(...),將async1().then@1推到微任務(wù)隊列中,取出async1().then@1,輸出:7【7】和 "async2的結(jié)果"【8】;

第一輪任務(wù)執(zhí)行完畢,開始執(zhí)行第二輪,此時:

第二輪:

開始執(zhí)行第二輪宏任務(wù),將setTimeOut@1取出執(zhí)行,輸出8【9】,完畢。

所以任務(wù)被執(zhí)行完畢,結(jié)果為:1 > 2 > 3 > 4 > 5 > 6 > 7 > async2的結(jié)果 > 8

------------------------ END ----------------------------

PS: 好記性不如爛筆頭,看了那么多資料,還是想總結(jié)一下,不然過一陣子就忘記了,如果辛苦指出哦,謝謝~

參考資料:

理解 JavaScript 的 async/await
瀏覽器和Node不同的事件循環(huán)(Event Loop)
Event Loop 原來是這么回事
這一次,徹底弄懂 JavaScript 執(zhí)行機制
從event loop到async await來了解事件循環(huán)機制
...

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

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

相關(guān)文章

  • 10分鐘理解JS引擎的執(zhí)行機制

    摘要:深入理解引擎的執(zhí)行機制靈魂三問為什么是單線程的為什么需要異步單線程又是如何實現(xiàn)異步的呢中的中的說說首先請牢記點是單線程語言的是的執(zhí)行機制。 深入理解JS引擎的執(zhí)行機制 1.靈魂三問 : JS為什么是單線程的? 為什么需要異步? 單線程又是如何實現(xiàn)異步的呢? 2.JS中的event loop(1) 3.JS中的event loop(2) 4.說說setTimeout 首先,請牢記2...

    zzbo 評論0 收藏0
  • JavaScript Event Loop 機制詳解與 Vue.js 中實踐應用

    摘要:機制詳解與中實踐應用歸納于筆者的現(xiàn)代開發(fā)語法基礎(chǔ)與實踐技巧系列文章。事件循環(huán)機制詳解與實踐應用是典型的單線程單并發(fā)語言,即表示在同一時間片內(nèi)其只能執(zhí)行單個任務(wù)或者部分代碼片。 JavaScript Event Loop 機制詳解與 Vue.js 中實踐應用歸納于筆者的現(xiàn)代 JavaScript 開發(fā):語法基礎(chǔ)與實踐技巧系列文章。本文依次介紹了函數(shù)調(diào)用棧、MacroTask 與 Micr...

    livem 評論0 收藏0
  • JavaScript 運行機制--Event Loop詳解

    摘要:上代碼代碼可以看出,不僅函數(shù)比指定的回調(diào)函數(shù)先執(zhí)行,而且函數(shù)也比先執(zhí)行。這是因為后一個事件進入的時候,事件環(huán)可能處于不同的階段導致結(jié)果的不確定。這是因為因為執(zhí)行完后,程序設(shè)定了和,因此階段不會被阻塞進而進入階段先執(zhí)行,后進入階段執(zhí)行。 JavaScript(簡稱JS)是前端的首要研究語言,要想真正理解JavaScript就繞不開他的運行機制--Event Loop(事件環(huán)) JS是一門...

    snifes 評論0 收藏0
  • 覽器和Node中的事件循環(huán)機制

    摘要:二瀏覽器端在講解事件循環(huán)之前先談?wù)勚型酱a異步代碼的執(zhí)行流程。三端我自己認為的事件循環(huán)和瀏覽器端還是有點區(qū)別的,它的事件循環(huán)依靠引擎。四總結(jié)本篇主要介紹了瀏覽器和對于事件循環(huán)機制實現(xiàn),由于能力水平有限,其中可能有誤之處歡迎指出。 一、前言 前幾天聽公司一個公司三年的前端說今天又學到了一個知識點-微任務(wù)、宏任務(wù),我問他這是什么東西,由于在吃飯他淺淺的說了下,當時沒太理解就私下學習整理一...

    KevinYan 評論0 收藏0
  • 覽器下的 Event Loop

    摘要:前言是以單線程的形式運行在宿主環(huán)境下,采用了回調(diào)的形式來解決異步任務(wù)。線程中步就是在瀏覽器下的。 前言 javascript 是以單線程的形式運行在宿主環(huán)境下,javascript 采用了回調(diào)的形式來解決異步任務(wù)。 為什么是單線程? javascript 的最開始的出現(xiàn)是為了給 web 頁面增添一些動態(tài)的效果,那么就避免不了獲取頁面上的元素信息,如果 javascript 是以多線程的...

    forrest23 評論0 收藏0

發(fā)表評論

0條評論

nemo

|高級講師

TA的文章

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