欧美一区二区三区老妇人-欧美做爰猛烈大尺度电-99久久夜色精品国产亚洲a-亚洲福利视频一区二区

JavaScript事件循環(huán)中微任務和宏任務有什么區(qū)別

JavaScript 事件循環(huán)中微任務和宏任務有什么區(qū)別,相信很多沒有經(jīng)驗的人對此束手無策,為此本文總結了問題出現(xiàn)的原因和解決方法,通過這篇文章希望你能解決這個問題。

成都創(chuàng)新互聯(lián)公司專注于企業(yè)全網(wǎng)營銷推廣、網(wǎng)站重做改版、江城網(wǎng)站定制設計、自適應品牌網(wǎng)站建設、H5開發(fā)購物商城網(wǎng)站建設、集團公司官網(wǎng)建設、外貿網(wǎng)站建設、高端網(wǎng)站制作、響應式網(wǎng)頁設計等建站業(yè)務,價格優(yōu)惠性價比高,為江城等各大城市提供網(wǎng)站開發(fā)制作服務。

事件循環(huán):微任務和宏任務

瀏覽器中 JavaScript 的執(zhí)行流程和 Node.js 中的流程都是基于 事件循環(huán) 的。

理解事件循環(huán)的工作方式對于代碼優(yōu)化很重要,有時對于正確的架構也很重要。

在本章中,我們首先介紹有關事件循環(huán)工作方式的理論細節(jié),然后介紹該知識的實際應用。

事件循環(huán)

事件循環(huán) 的概念非常簡單。它是一個在 JavaScript 引擎等待任務,執(zhí)行任務和進入休眠狀態(tài)等待更多任務這幾個狀態(tài)之間轉換的無限循環(huán)。

引擎的一般算法:

1.當有任務時:

  • 從最先進入的任務開始執(zhí)行。

2.休眠直到出現(xiàn)任務,然后轉到第 1 步。

當我們?yōu)g覽一個網(wǎng)頁時就是上述這種形式。JavaScript 引擎大多數(shù)時候不執(zhí)行任何操作,它僅在腳本/處理程序/事件激活時執(zhí)行。

任務示例:

  • 當外部腳本<script src="..."> 加載完成時,任務就是執(zhí)行它。

  • 當用戶移動鼠標時,任務就是派生出 mousemove 事件和執(zhí)行處理程序。

  • 當安排的(scheduled)setTimeout 時間到達時,任務就是執(zhí)行其回調。

  • &hellip;&hellip;諸如此類。

設置任務 &mdash;&mdash; 引擎處理它們 &mdash;&mdash; 然后等待更多任務(即休眠,幾乎不消耗 CPU 資源)。

一個任務到來時,引擎可能正處于繁忙狀態(tài),那么這個任務就會被排入隊列。

多個任務組成了一個隊列,即所謂的“宏任務隊列”(v8 術語):

JavaScript 事件循環(huán)中微任務和宏任務有什么區(qū)別

例如,當引擎正在忙于執(zhí)行一段 script 時,用戶可能會移動鼠標而產(chǎn)生 mousemove 事件,setTimeout  或許也剛好到期,以及其他任務,這些任務組成了一個隊列,如上圖所示。

隊列中的任務基于“先進先出”的原則執(zhí)行。當瀏覽器引擎執(zhí)行完 script 后,它會處理 mousemove 事件,然后處理 setTimeout  處理程序,依此類推。

到目前為止,很簡單,對吧?

兩個細節(jié):

  1. 鴻蒙官方戰(zhàn)略合作共建——HarmonyOS技術社區(qū)

  2. 引擎執(zhí)行任務時永遠不會進行渲染(render)。如果任務執(zhí)行需要很長一段時間也沒關系。僅在任務完成后才會繪制對 DOM 的更改。

  3. 如果一項任務執(zhí)行花費的時間過長,瀏覽器將無法執(zhí)行其他任務,例如處理用戶事件。因此,在一定時間后,瀏覽器會拋出一個如“頁面未響應”之類的警報,建議你終止這個任務。這種情況常發(fā)生在有大量復雜的計算或導致死循環(huán)的程序錯誤時。

以上是理論知識。現(xiàn)在,讓我們來看看如何應用這些知識。

用例 1:拆分 CPU 過載任務

假設我們有一個 CPU 過載任務。

例如,語法高亮(用來給本頁面中的示例代碼著色)是相當耗費 CPU 資源的任務。為了高亮顯示代碼,它執(zhí)行分析,創(chuàng)建很多著了色的元素,然后將它們添加到文檔中  &mdash;&mdash; 對于文本量大的文檔來說,需要耗費很長時間。

當引擎忙于語法高亮時,它就無法處理其他 DOM  相關的工作,例如處理用戶事件等。它甚至可能會導致瀏覽器“中斷(hiccup)”甚至“掛起(hang)”一段時間,這是不可接受的。

我們可以通過將大任務拆分成多個小任務來避免這個問題。高亮顯示前 100 行,然后使用 setTimeout(延時參數(shù)為 0)來安排(schedule)后  100 行的高亮顯示,依此類推。

為了演示這種方法,簡單起見,讓我們寫一個從 1 數(shù)到 1000000000 的函數(shù),而不寫文本高亮。

如果你運行下面這段代碼,你會看到引擎會“掛起”一段時間。對于服務端 JS  來說這顯而易見,并且如果你在瀏覽器中運行它,嘗試點擊頁面上其他按鈕時,你會發(fā)現(xiàn)在計數(shù)結束之前不會處理其他事件。

let i = 0;  let start = Date.now();  function count() {    // 做一個繁重的任務   for (let j = 0; j < 1e9; j++) {     i++;   }    alert("Done in " + (Date.now() - start) + 'ms'); }  count();

瀏覽器甚至可能會顯示一個“腳本執(zhí)行時間過長”的警告。

讓我們使用嵌套的 setTimeout 調用來拆分這個任務:

let i = 0;  let start = Date.now();  function count() {    // 做繁重的任務的一部分 (*)   do {     i++;   } while (i % 1e6 != 0);    if (i == 1e9) {     alert("Done in " + (Date.now() - start) + 'ms');   } else {     setTimeout(count); // 安排(schedule)新的調用 (**)   }  }  count();

現(xiàn)在,瀏覽器界面在“計數(shù)”過程中可以正常使用。

單次執(zhí)行 count 會完成工作 (*) 的一部分,然后根據(jù)需要重新安排(schedule)自身的執(zhí)行 (**):

  1. 鴻蒙官方戰(zhàn)略合作共建——HarmonyOS技術社區(qū)

  2. 首先執(zhí)行計數(shù):i=1...1000000。

  3. 然后執(zhí)行計數(shù):i=1000001..2000000。

  4. &hellip;&hellip;以此類推。

現(xiàn)在,如果在引擎忙于執(zhí)行第一部分時出現(xiàn)了一個新的副任務(例如 onclick  事件),則該任務會被排入隊列,然后在第一部分執(zhí)行結束時,并在下一部分開始執(zhí)行前,會執(zhí)行該副任務。周期性地在兩次 count 執(zhí)行期間返回事件循環(huán),這為  JavaScript 引擎提供了足夠的“空氣”來執(zhí)行其他操作,以響應其他的用戶行為。

值得注意的是這兩種變體 &mdash;&mdash; 是否使用了 setTimeout 對任務進行拆分 &mdash;&mdash; 在執(zhí)行速度上是相當?shù)摹T趫?zhí)行計數(shù)的總耗時上沒有多少差異。

為了使兩者耗時更接近,讓我們來做一個改進。

我們將要把調度(scheduling)移動到 count() 的開頭:

let i = 0;  let start = Date.now();  function count() {    // 將調度(scheduling)移動到開頭   if (i < 1e9 - 1e6) {     setTimeout(count); // 安排(schedule)新的調用   }    do {     i++;   } while (i % 1e6 != 0);    if (i == 1e9) {     alert("Done in " + (Date.now() - start) + 'ms');   }  }  count();

現(xiàn)在,當我們開始調用 count() 時,會看到我們需要對 count() 進行更多調用,我們就會在工作前立即安排(schedule)它。

如果你運行它,你很容易注意到它花費的時間明顯減少了。

為什么?

這很簡單:你應該還記得,多個嵌套的 setTimeout 調用在瀏覽器中的最小延遲為 4ms。即使我們設置了 0,但還是  4ms(或者更久一些)。所以我們安排(schedule)得越早,運行速度也就越快。

最后,我們將一個繁重的任務拆分成了幾部分,現(xiàn)在它不會阻塞用戶界面了。而且其總耗時并不會長很多。

用例 2:進度指示

對瀏覽器腳本中的過載型任務進行拆分的另一個好處是,我們可以顯示進度指示。

正如前面所提到的,僅在當前運行的任務完成后,才會對 DOM 中的更改進行繪制,無論這個任務運行花費了多長時間。

從一方面講,這非常好,因為我們的函數(shù)可能會創(chuàng)建很多元素,將它們一個接一個地插入到文檔中,并更改其樣式 &mdash;&mdash;  訪問者不會看到任何未完成的“中間態(tài)”內容。很重要,對吧?

這是一個示例,對 i 的更改在該函數(shù)完成前不會顯示出來,所以我們將只會看到最后的值:

<div id="progress"></div>  <script>    function count() {     for (let i = 0; i < 1e6; i++) {       i++;       progress.innerHTML = i;     }   }    count(); </script>

&hellip;&hellip;但是我們也可能想在任務執(zhí)行期間展示一些東西,例如進度條。

如果我們使用 setTimeout 將繁重的任務拆分成幾部分,那么變化就會被在它們之間繪制出來。

這看起來更好看:

<div id="progress"></div>  <script>   let i = 0;    function count() {      // 做繁重的任務的一部分 (*)     do {       i++;       progress.innerHTML = i;     } while (i % 1e3 != 0);      if (i < 1e7) {       setTimeout(count);     }    }    count(); </script>

現(xiàn)在 div 顯示了 i 的值的增長,這就是進度條的一種。

用例 3:在事件之后做一些事情

在事件處理程序中,我們可能會決定推遲某些行為,直到事件冒泡并在所有級別上得到處理后。我們可以通過將該代碼包裝到零延遲的 setTimeout  中來做到這一點。

在 創(chuàng)建自定義事件[1] 一章中,我們看到過這樣一個例子:自定義事件 menu-open 被在 setTimeout  中分派(dispatched),所以它在 click 事件被處理完成之后發(fā)生。

menu.onclick = function() {   // ...    // 創(chuàng)建一個具有被點擊的菜單項的數(shù)據(jù)的自定義事件   let customEvent = new CustomEvent("menu-open", {     bubbles: true   });    // 異步分派(dispatch)自定義事件   setTimeout(() => menu.dispatchEvent(customEvent)); };

宏任務和微任務

除了本章中所講的 宏任務(macrotask) 外,還有在 微任務隊列[2] 一章中提到的 微任務(microtask)。

微任務僅來自于我們的代碼。它們通常是由 promise 創(chuàng)建的:對 .then/catch/finally 處理程序的執(zhí)行會成為微任務。微任務也被用于  await 的“幕后”,因為它是 promise 處理的另一種形式。

還有一個特殊的函數(shù) queueMicrotask(func),它對 func 進行排隊,以在微任務隊列中執(zhí)行。

每個宏任務之后,引擎會立即執(zhí)行微任務隊列中的所有任務,然后再執(zhí)行其他的宏任務,或渲染,或進行其他任何操作。

例如,看看下面這個示例:

setTimeout(() => alert("timeout"));  Promise.resolve()   .then(() => alert("promise"));  alert("code");

這里的執(zhí)行順序是怎樣的?

  1. 鴻蒙官方戰(zhàn)略合作共建——HarmonyOS技術社區(qū)

  2. code 首先顯示,因為它是常規(guī)的同步調用。

  3. promise 第二個出現(xiàn),因為 then 會通過微任務隊列,并在當前代碼之后執(zhí)行。

  4. timeout 最后顯示,因為它是一個宏任務。

更詳細的事件循環(huán)圖示如下(順序是從上到下,即:首先是腳本,然后是微任務,渲染等):

JavaScript 事件循環(huán)中微任務和宏任務有什么區(qū)別

微任務會在執(zhí)行任何其他事件處理,或渲染,或執(zhí)行任何其他宏任務之前完成。

這很重要,因為它確保了微任務之間的應用程序環(huán)境基本相同(沒有鼠標坐標更改,沒有新的網(wǎng)絡數(shù)據(jù)等)。

如果我們想要異步執(zhí)行(在當前代碼之后)一個函數(shù),但是要在更改被渲染或新事件被處理之前執(zhí)行,那么我們可以使用 queueMicrotask  來對其進行安排(schedule)。

這是一個與前面那個例子類似的,帶有“計數(shù)進度條”的示例,但是它使用了 queueMicrotask而不是  setTimeout。你可以看到它在最后才渲染。就像寫的是同步代碼一樣:

<div id="progress"></div>  <script>   let i = 0;    function count() {      // 做繁重的任務的一部分 (*)     do {       i++;       progress.innerHTML = i;     } while (i % 1e3 != 0);      if (i < 1e6) {       queueMicrotask(count);     }    }    count(); </script>

總結

更詳細的事件循環(huán)算法(盡管與 規(guī)范[3] 相比仍然是簡化過的):

1.從 宏任務 隊列(例如 "script")中出隊(dequeue)并執(zhí)行最早的任務。

2.執(zhí)行所有 微任務:

  • 出隊(dequeue)并執(zhí)行最早的微任務。

  • 當微任務隊列非空時:

3.執(zhí)行渲染,如果有。

4.如果宏任務隊列為空,則休眠直到出現(xiàn)宏任務。

5.轉到步驟 1。

安排(schedule)一個新的 宏任務:

  • 使用零延遲的 setTimeout(f)。

它可被用于將繁重的計算任務拆分成多個部分,以使瀏覽器能夠對用戶事件作出反應,并在任務的各部分之間顯示任務進度。

此外,也被用于在事件處理程序中,將一個行為(action)安排(schedule)在事件被完全處理(冒泡完成)后。

安排一個新的 微任務:

  • 使用 queueMicrotask(f)。

  • promise 處理程序也會通過微任務隊列。

在微任務之間沒有 UI 或網(wǎng)絡事件的處理:它們一個立即接一個地執(zhí)行。

所以,我們可以使用 queueMicrotask 來在保持環(huán)境狀態(tài)一致的情況下,異步地執(zhí)行一個函數(shù)。

Web Workers:

對于不應該阻塞事件循環(huán)的耗時長的繁重計算任務,我們可以使用 Web Workers[4]。

這是在另一個并行線程中運行代碼的方式。

Web Workers 可以與主線程交換消息,但是它們具有自己的變量和事件循環(huán)。

Web Workers 沒有訪問 DOM 的權限,因此,它們對于同時使用多個 CPU 內核的計算非常有用。

看完上述內容,你們掌握 JavaScript 事件循環(huán)中微任務和宏任務有什么區(qū)別的方法了嗎?如果還想學到更多技能或想了解更多相關內容,歡迎關注創(chuàng)新互聯(lián)行業(yè)資訊頻道,感謝各位的閱讀!

網(wǎng)頁標題:JavaScript事件循環(huán)中微任務和宏任務有什么區(qū)別
文章源于:http://chinadenli.net/article10/gghcdo.html

成都網(wǎng)站建設公司_創(chuàng)新互聯(lián),為您提供小程序開發(fā)用戶體驗網(wǎng)站內鏈網(wǎng)站收錄定制網(wǎng)站標簽優(yōu)化

廣告

聲明:本網(wǎng)站發(fā)布的內容(圖片、視頻和文字)以用戶投稿、用戶轉載內容為主,如果涉及侵權請盡快告知,我們將會在第一時間刪除。文章觀點不代表本網(wǎng)站立場,如需處理請聯(lián)系客服。電話:028-86922220;郵箱:631063699@qq.com。內容未經(jīng)允許不得轉載,或轉載時需注明來源: 創(chuàng)新互聯(lián)

成都網(wǎng)站建設