国产成人精品18p,天天干成人网,无码专区狠狠躁天天躁,美女脱精光隐私扒开免费观看

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

發(fā)布時(shí)間:2021-07-03 23:03 來(lái)源:億速云 閱讀:0 作者:Leah 欄目: 開(kāi)發(fā)技術(shù)

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

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

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

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

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

事件循環(huán)

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

引擎的一般算法:

1.當有任務(wù)時(shí):

  • 從最先進(jìn)入的任務(wù)開(kāi)始執行。

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

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

任務(wù)示例:

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

  • 當用戶(hù)移動(dòng)鼠標時(shí),任務(wù)就是派生出 mousemove 事件和執行處理程序。

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

  • &hellip;&hellip;諸如此類(lèi)。

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

一個(gè)任務(wù)到來(lái)時(shí),引擎可能正處于繁忙狀態(tài),那么這個(gè)任務(wù)就會(huì )被排入隊列。

多個(gè)任務(wù)組成了一個(gè)隊列,即所謂的“宏任務(wù)隊列”(v8 術(shù)語(yǔ)):

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

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

到目前為止,很簡(jiǎn)單,對吧?

兩個(gè)細節:

  1. 鴻蒙官方戰略合作共建——HarmonyOS技術(shù)社區

  2. 引擎執行任務(wù)時(shí)永遠不會(huì )進(jìn)行渲染(render)。如果任務(wù)執行需要很長(cháng)一段時(shí)間也沒(méi)關(guān)系。僅在任務(wù)完成后才會(huì )繪制對 DOM 的更改。

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

以上是理論知識?,F在,讓我們來(lái)看看如何應用這些知識。

用例 1:拆分 CPU 過(guò)載任務(wù)

假設我們有一個(gè) CPU 過(guò)載任務(wù)。

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

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

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

為了演示這種方法,簡(jiǎn)單起見(jiàn),讓我們寫(xiě)一個(gè)從 1 數到 1000000000 的函數,而不寫(xiě)文本高亮。

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

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

瀏覽器甚至可能會(huì )顯示一個(gè)“腳本執行時(shí)間過(guò)長(cháng)”的警告。

讓我們使用嵌套的 setTimeout 調用來(lái)拆分這個(gè)任務(wù):

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

現在,瀏覽器界面在“計數”過(guò)程中可以正常使用。

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

  1. 鴻蒙官方戰略合作共建——HarmonyOS技術(shù)社區

  2. 首先執行計數:i=1...1000000。

  3. 然后執行計數:i=1000001..2000000。

  4. &hellip;&hellip;以此類(lèi)推。

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

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

為了使兩者耗時(shí)更接近,讓我們來(lái)做一個(gè)改進(jìn)。

我們將要把調度(scheduling)移動(dòng)到 count() 的開(kāi)頭:

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

現在,當我們開(kāi)始調用 count() 時(shí),會(huì )看到我們需要對 count() 進(jìn)行更多調用,我們就會(huì )在工作前立即安排(schedule)它。

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

為什么?

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

最后,我們將一個(gè)繁重的任務(wù)拆分成了幾部分,現在它不會(huì )阻塞用戶(hù)界面了。而且其總耗時(shí)并不會(huì )長(cháng)很多。

用例 2:進(jìn)度指示

對瀏覽器腳本中的過(guò)載型任務(wù)進(jìn)行拆分的另一個(gè)好處是,我們可以顯示進(jìn)度指示。

正如前面所提到的,僅在當前運行的任務(wù)完成后,才會(huì )對 DOM 中的更改進(jìn)行繪制,無(wú)論這個(gè)任務(wù)運行花費了多長(cháng)時(shí)間。

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

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

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

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

如果我們使用 setTimeout 將繁重的任務(wù)拆分成幾部分,那么變化就會(huì )被在它們之間繪制出來(lái)。

這看起來(lái)更好看:

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

現在 div 顯示了 i 的值的增長(cháng),這就是進(jìn)度條的一種。

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

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

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

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

宏任務(wù)和微任務(wù)

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

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

還有一個(gè)特殊的函數 queueMicrotask(func),它對 func 進(jìn)行排隊,以在微任務(wù)隊列中執行。

每個(gè)宏任務(wù)之后,引擎會(huì )立即執行微任務(wù)隊列中的所有任務(wù),然后再執行其他的宏任務(wù),或渲染,或進(jìn)行其他任何操作。

例如,看看下面這個(gè)示例:

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

這里的執行順序是怎樣的?

  1. 鴻蒙官方戰略合作共建——HarmonyOS技術(shù)社區

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

  3. promise 第二個(gè)出現,因為 then 會(huì )通過(guò)微任務(wù)隊列,并在當前代碼之后執行。

  4. timeout 最后顯示,因為它是一個(gè)宏任務(wù)。

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

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

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

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

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

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

總結

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

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

2.執行所有 微任務(wù):

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

  • 當微任務(wù)隊列非空時(shí):

3.執行渲染,如果有。

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

5.轉到步驟 1。

安排(schedule)一個(gè)新的 宏任務(wù):

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

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

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

安排一個(gè)新的 微任務(wù):

  • 使用 queueMicrotask(f)。

  • promise 處理程序也會(huì )通過(guò)微任務(wù)隊列。

在微任務(wù)之間沒(méi)有 UI 或網(wǎng)絡(luò )事件的處理:它們一個(gè)立即接一個(gè)地執行。

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

Web Workers:

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

這是在另一個(gè)并行線(xiàn)程中運行代碼的方式。

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

Web Workers 沒(méi)有訪(fǎng)問(wèn) DOM 的權限,因此,它們對于同時(shí)使用多個(gè) CPU 內核的計算非常有用。

免責聲明:本站發(fā)布的內容(圖片、視頻和文字)以原創(chuàng )、來(lái)自互聯(lián)網(wǎng)轉載和分享為主,文章觀(guān)點(diǎn)不代表本網(wǎng)站立場(chǎng),如果涉及侵權請聯(lián)系QQ:712375056 進(jìn)行舉報,并提供相關(guān)證據,一經(jīng)查實(shí),將立刻刪除涉嫌侵權內容。

久久中文字幕无码专区| 国色天香卡一卡二乱码| 亚欧乱色熟女一区二区三区| 婷婷成人综合激情在线视频播放| H无码精品动漫在线观看免费| 国内精品综合久久久40P|