最新国产好看的视频,伊人天堂AV在线,国产Aaaaaa视频,蜜臀视频在线观看一区,人妻av色图,密臀久久久精品影片,青青视频免费观看毛片,久草在线观看视,国产三级精品色情在线

JavaScript事件循環(huán)之宏任務(wù)與微任務(wù)的執(zhí)行機(jī)制深入理解

 更新時(shí)間:2025年12月21日 10:39:19   作者:fruge365  
JavaScript的事件循環(huán)機(jī)制是理解異步編程和性能優(yōu)化的關(guān)鍵,下面這篇文章主要介紹了JavaScript事件循環(huán)之宏任務(wù)與微任務(wù)執(zhí)行機(jī)制的相關(guān)資料,文中通過(guò)代碼介紹的非常詳細(xì),需要的朋友可以參考下

JavaScript 是單線程語(yǔ)言,但它卻能處理復(fù)雜的并發(fā)操作(如網(wǎng)絡(luò)請(qǐng)求、定時(shí)器、用戶交互),這背后的秘密武器就是 事件循環(huán)(Event Loop)。本文將深入拆解宏任務(wù)與微任務(wù)的執(zhí)行邏輯,通過(guò)代碼示例幫你徹底搞懂執(zhí)行順序。

TL;DR

  • 核心機(jī)制:JS 引擎執(zhí)行同步代碼 -> 清空微任務(wù)隊(duì)列 -> 嘗試 DOM 渲染 -> 執(zhí)行一個(gè)宏任務(wù) -> 清空微任務(wù)隊(duì)列 -> … 循環(huán)往復(fù)。
  • 微任務(wù)(MicroTask):優(yōu)先級(jí)高,在當(dāng)前宏任務(wù)結(jié)束后立即執(zhí)行。包括 Promise.then、process.nextTick (Node)、MutationObserver。
  • 宏任務(wù)(MacroTask):優(yōu)先級(jí)低,每次循環(huán)只執(zhí)行一個(gè)。包括 setTimeoutsetInterval、setImmediate (Node)、I/O、UI Rendering。
  • 關(guān)鍵點(diǎn):微任務(wù)隊(duì)列總是會(huì)在下一個(gè)宏任務(wù)開(kāi)始之前被清空。

1. 為什么需要事件循環(huán)?

JavaScript 的設(shè)計(jì)初衷是作為瀏覽器腳本語(yǔ)言,主要用途是與用戶互動(dòng)和操作 DOM。如果它是多線程的,一個(gè)線程在刪除 DOM 節(jié)點(diǎn),另一個(gè)線程在編輯該節(jié)點(diǎn),會(huì)帶來(lái)復(fù)雜的同步問(wèn)題。因此,JS 選擇 單線程 執(zhí)行。

為了不阻塞主線程(例如等待一個(gè) 5秒的 API 請(qǐng)求),JS 引入了 異步非阻塞 機(jī)制,而事件循環(huán)正是協(xié)調(diào)同步代碼與異步回調(diào)執(zhí)行順序的調(diào)度員。

2. 宏任務(wù)與微任務(wù)的分類

并不是所有的異步任務(wù)都是一樣的。它們被分為兩類隊(duì)列:

微任務(wù) (MicroTask)

通常是由代碼本身產(chǎn)生的任務(wù),優(yōu)先級(jí)較高,需要在當(dāng)前同步代碼執(zhí)行完后立即處理。

  • Promise.then / .catch / .finally
  • process.nextTick (Node.js 環(huán)境,優(yōu)先級(jí)高于 Promise)
  • MutationObserver (監(jiān)聽(tīng) DOM 變化)
  • queueMicrotask API

宏任務(wù) (MacroTask)

通常是由宿主環(huán)境(瀏覽器或 Node)發(fā)起的任務(wù),每次事件循環(huán)只取一個(gè)執(zhí)行。

  • setTimeout / setInterval
  • setImmediate (Node.js)
  • requestAnimationFrame (UI 渲染前執(zhí)行,歸類有些特殊,通常視為渲染階段的一部分)
  • I/O 操作 (文件讀寫(xiě)、網(wǎng)絡(luò)請(qǐng)求回調(diào))
  • UI Rendering (瀏覽器繪制)
  • <script> (整體代碼本身算第一個(gè)宏任務(wù))

3. 事件循環(huán)的完整流程

標(biāo)準(zhǔn)的 Event Loop 流程如下:

  1. 執(zhí)行同步代碼(這本身屬于第一個(gè)宏任務(wù))。
  2. 檢查微任務(wù)隊(duì)列
    • 如果隊(duì)列不為空,取出隊(duì)首任務(wù)執(zhí)行。
    • 執(zhí)行過(guò)程中如果產(chǎn)生了新的微任務(wù),追加到隊(duì)尾,繼續(xù)執(zhí)行直到隊(duì)列清空。
  3. UI 渲染階段(瀏覽器視情況決定是否渲染):
    • 檢查是否需要更新 UI。
    • 執(zhí)行 requestAnimationFrame 回調(diào)(如果在渲染前)。
  4. 執(zhí)行宏任務(wù)
    • 從宏任務(wù)隊(duì)列中取出一個(gè)任務(wù)執(zhí)行。
    • 執(zhí)行完后,回到第 2 步(再次清空微任務(wù))。

口訣:同步走完清微任務(wù),渲染之后取宏任務(wù)。

4. 實(shí)戰(zhàn)代碼解析

案例一:基礎(chǔ)順序

console.log('1'); // 同步

setTimeout(() => {
  console.log('2'); // 宏任務(wù)
}, 0);

Promise.resolve().then(() => {
  console.log('3'); // 微任務(wù)
});

console.log('4'); // 同步

解析

  1. 執(zhí)行同步代碼:打印 '1',打印 '4'。
  2. 清空微任務(wù):執(zhí)行 Promise 回調(diào),打印 '3'。
  3. 執(zhí)行宏任務(wù):執(zhí)行 setTimeout 回調(diào),打印 '2'。
    結(jié)果1 -> 4 -> 3 -> 2

案例二:微任務(wù)插隊(duì)與嵌套

console.log('Start');

setTimeout(() => {
  console.log('Timeout'); // 宏任務(wù)
}, 0);

Promise.resolve().then(() => {
  console.log('Promise 1'); // 微任務(wù) 1
  // 微任務(wù)中產(chǎn)生新的微任務(wù)
  Promise.resolve().then(() => {
    console.log('Promise 2'); // 微任務(wù) 2
  });
});

console.log('End');

解析

  1. 同步打印 'Start', 'End'
  2. 檢查微任務(wù)隊(duì)列:發(fā)現(xiàn) Promise 1,執(zhí)行并打印。
  3. Promise 1 執(zhí)行時(shí)注冊(cè)了 Promise 2,追加到當(dāng)前微任務(wù)隊(duì)列尾部。
  4. 微任務(wù)隊(duì)列未空,繼續(xù)執(zhí)行 Promise 2,打印。
  5. 微任務(wù)清空完畢,去宏任務(wù)隊(duì)列取 Timeout 執(zhí)行。
    結(jié)果Start -> End -> Promise 1 -> Promise 2 -> Timeout

案例三:async/await 的本質(zhì)

async/await 只是 Promise 的語(yǔ)法糖。await 這一行右邊的代碼是同步執(zhí)行的,await 下面的代碼 相當(dāng)于放在了 Promise.then 中,屬于微任務(wù)。

async function async1() {
  console.log('async1 start');
  await async2(); 
  // 下面這行相當(dāng)于 .then(() => console.log('async1 end'))
  console.log('async1 end'); 
}

async function async2() {
  console.log('async2');
}

console.log('script start');

setTimeout(function() {
  console.log('setTimeout');
}, 0);

async1();

new Promise(function(resolve) {
  console.log('promise1'); // Promise 構(gòu)造函數(shù)內(nèi)是同步的
  resolve();
}).then(function() {
  console.log('promise2');
});

console.log('script end');

深度解析

  1. script start (同步)
  2. setTimeout 注冊(cè)宏任務(wù)。
  3. 調(diào)用 async1:
    • 打印 async1 start (同步)。
    • 調(diào)用 async2,打印 async2 (同步)。
    • 遇到 await,將 async1 end 放入微任務(wù)隊(duì)列 (微任務(wù)1)。
  4. new Promise:
    • 打印 promise1 (同步)。
    • resolve() 觸發(fā) then,將 promise2 放入微任務(wù)隊(duì)列 (微任務(wù)2)。
  5. 打印 script end (同步)。
  6. 同步結(jié)束,清空微任務(wù)
    • 執(zhí)行微任務(wù)1:打印 async1 end。
    • 執(zhí)行微任務(wù)2:打印 promise2。
  7. 微任務(wù)空,執(zhí)行宏任務(wù)
    • 打印 setTimeout。

結(jié)果
script start -> async1 start -> async2 -> promise1 -> script end -> async1 end -> promise2 -> setTimeout

(注:舊版 Chrome 曾有 Bug 導(dǎo)致 async1 end 比 promise2 慢,但在現(xiàn)代瀏覽器中已符合標(biāo)準(zhǔn),遵循入隊(duì)順序)

5. 易錯(cuò)點(diǎn)與注意事項(xiàng)

  1. Promise 構(gòu)造函數(shù)是同步的new Promise(fn) 中的 fn 會(huì)立即執(zhí)行,只有 .then 中的回調(diào)才是微任務(wù)。
  2. 微任務(wù)餓死宏任務(wù):如果你在微任務(wù)中無(wú)限循環(huán)地添加新的微任務(wù)(例如遞歸 Promise),那么主線程會(huì)一直被占用,宏任務(wù)永遠(yuǎn)無(wú)法執(zhí)行,頁(yè)面會(huì)卡死(類似 while(true))。
  3. UI 渲染時(shí)機(jī):通常瀏覽器會(huì)在清空微任務(wù)之后、執(zhí)行下一個(gè)宏任務(wù)之前嘗試渲染。如果微任務(wù)執(zhí)行時(shí)間過(guò)長(zhǎng),會(huì)阻塞渲染導(dǎo)致掉幀。
  4. Node.js 的差異
    • process.nextTick 優(yōu)先級(jí)高于 Promise。
    • 早期的 Node (v10及以前) 在執(zhí)行完一個(gè)階段的所有宏任務(wù)后才清空微任務(wù),但 Node v11+ 已修改為與瀏覽器一致:每執(zhí)行完一個(gè)宏任務(wù)就清空一次微任務(wù)

總結(jié)

掌握事件循環(huán)的關(guān)鍵在于分清 同步代碼、微任務(wù)宏任務(wù) 的層級(jí)。始終記?。何⑷蝿?wù)是 VIP 通道,必須優(yōu)先走完;宏任務(wù)是普通通道,一次只能走一個(gè)。

到此這篇關(guān)于JavaScript事件循環(huán)之宏任務(wù)與微任務(wù)執(zhí)行機(jī)制的文章就介紹到這了,更多相關(guān)JS宏任務(wù)與微任務(wù)執(zhí)行機(jī)制內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!

相關(guān)文章

最新評(píng)論

原阳县| 措美县| 通道| 吉水县| 大理市| 报价| 长汀县| 贺兰县| 安图县| 诸城市| 江孜县| 娄烦县| 马公市| 临沭县| 吴桥县| 通海县| 桃江县| 乌拉特后旗| 留坝县| 茶陵县| 郸城县| 扬中市| 商南县| 南江县| 牙克石市| 泊头市| 忻城县| 诸暨市| 汶川县| 兴安盟| 鹤山市| 建德市| 施甸县| 报价| 三明市| 蓬溪县| 偏关县| 铜鼓县| 威信县| 曲麻莱县| 永安市|