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

JavaScript中的事件循環(huán)機(jī)制解讀

 更新時(shí)間:2025年10月28日 16:45:46   作者:x-z-y  
本文全面介紹了JavaScript的事件循環(huán)機(jī)制,包括其基本概念、組成部分、任務(wù)隊(duì)列的詳細(xì)解釋、完整的執(zhí)行流程、實(shí)際代碼演示、瀏覽器與Node.js的區(qū)別、常見(jiàn)面試題分析以及性能優(yōu)化技巧,通過(guò)記憶口訣“同微宏,微先走,宏之后,微清空”,幫助讀者更好地理解和應(yīng)用事件循環(huán)機(jī)制

1. 事件循環(huán)基本概念

1.1 什么是事件循環(huán)?

事件循環(huán)是 JavaScript 處理異步任務(wù)的核心機(jī)制,它讓單線程的 JavaScript 能夠"同時(shí)"處理多個(gè)任務(wù)。

console.log('1. 開(kāi)始');

setTimeout(() => {
    console.log('2. 定時(shí)器');
}, 0);

Promise.resolve().then(() => {
    console.log('3. Promise');
});

console.log('4. 結(jié)束');

// 執(zhí)行順序:1 → 4 → 3 → 2
// 這就是事件循環(huán)在起作用!

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

JavaScript 是單線程的,如果沒(méi)有事件循環(huán),遇到耗時(shí)任務(wù)時(shí)頁(yè)面就會(huì)卡死。

// 如果沒(méi)有事件循環(huán) - 糟糕的情況
console.log('開(kāi)始');
const result = synchronousNetworkRequest(); // 假設(shè)這個(gè)請(qǐng)求需要3秒
console.log('收到結(jié)果:', result);
console.log('頁(yè)面卡住了3秒!用戶無(wú)法操作!');

// 有事件循環(huán) - 良好的體驗(yàn)
console.log('開(kāi)始');
asynchronousNetworkRequest((result) => {
    console.log('收到結(jié)果:', result);
});
console.log('頁(yè)面可以立即響應(yīng)!用戶可以正常操作!');

2. 事件循環(huán)的組成部分

2.1 三大核心組件

// 可視化事件循環(huán)結(jié)構(gòu)
┌───────────────────────────┐
│       調(diào)用棧 (Call Stack)     │ ← 正在執(zhí)行的代碼
└───────────────────────────┘
             ↓
┌───────────────────────────┐
│      Web APIs 環(huán)境          │ ← 瀏覽器提供的異步API
│   - setTimeout          │
│   - DOM事件              │
│   - 網(wǎng)絡(luò)請(qǐng)求              │
└───────────────────────────┘
             ↓
┌───────────────────────────┐
│       任務(wù)隊(duì)列 (Task Queue)   │ ← 等待執(zhí)行的回調(diào)函數(shù)
│   1. 宏任務(wù)隊(duì)列              │
│   2. 微任務(wù)隊(duì)列              │
└───────────────────────────┘
             ↓
┌───────────────────────────┐
│       事件循環(huán) (Event Loop)   │ ← 協(xié)調(diào)調(diào)度的"管理員"
└───────────────────────────┘

2.2 調(diào)用棧 (Call Stack)

function first() {
    console.log('第一個(gè)函數(shù)開(kāi)始');
    second();
    console.log('第一個(gè)函數(shù)結(jié)束');
}

function second() {
    console.log('第二個(gè)函數(shù)開(kāi)始');
    third();
    console.log('第二個(gè)函數(shù)結(jié)束');
}

function third() {
    console.log('第三個(gè)函數(shù)');
}

first();

// 調(diào)用棧變化:
// 1. first() 入棧
// 2. console.log() 入棧 → 執(zhí)行 → 出棧
// 3. second() 入棧
// 4. console.log() 入棧 → 執(zhí)行 → 出棧
// 5. third() 入棧
// 6. console.log() 入棧 → 執(zhí)行 → 出棧
// 7. third() 出棧
// 8. console.log() 入棧 → 執(zhí)行 → 出棧
// 9. second() 出棧
// 10. first() 出棧

3. 任務(wù)隊(duì)列詳解

3.1 微任務(wù) (Microtasks) vs 宏任務(wù) (Macrotasks)

console.log('腳本開(kāi)始'); // 同步任務(wù)

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

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

// 另一個(gè)微任務(wù)
queueMicrotask(() => {
    console.log('queueMicrotask - 微任務(wù)');
});

console.log('腳本結(jié)束'); // 同步任務(wù)

// 執(zhí)行順序:
// 1. 腳本開(kāi)始 (同步)
// 2. 腳本結(jié)束 (同步)
// 3. Promise - 微任務(wù)
// 4. queueMicrotask - 微任務(wù)
// 5. setTimeout - 宏任務(wù)

3.2 微任務(wù)有哪些?

// 常見(jiàn)的微任務(wù)來(lái)源:
Promise.then() / .catch() / .finally()
queueMicrotask()
MutationObserver(DOM變化觀察)
process.nextTick(Node.js)

// 微任務(wù)特點(diǎn):優(yōu)先級(jí)高,在每個(gè)宏任務(wù)之后立即執(zhí)行

3.3 宏任務(wù)有哪些?

// 常見(jiàn)的宏任務(wù)來(lái)源:
setTimeout / setInterval
setImmediate(Node.js)
I/O 操作(文件讀取、網(wǎng)絡(luò)請(qǐng)求)
UI 渲染(瀏覽器)
DOM 事件(click、load等)

// 宏任務(wù)特點(diǎn):優(yōu)先級(jí)較低,等待調(diào)用棧清空后執(zhí)行

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

4.1 詳細(xì)執(zhí)行步驟

// 步驟演示
console.log('1. 同步任務(wù)開(kāi)始');

// 宏任務(wù)
setTimeout(() => {
    console.log('6. 宏任務(wù) - setTimeout');
    Promise.resolve().then(() => {
        console.log('7. 微任務(wù) - 在宏任務(wù)中');
    });
}, 0);

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

Promise.resolve().then(() => {
    console.log('5. 微任務(wù) - Promise 2');
});

console.log('2. 同步任務(wù)繼續(xù)');

setTimeout(() => {
    console.log('8. 另一個(gè)宏任務(wù)');
}, 0);

console.log('3. 同步任務(wù)結(jié)束');

// 執(zhí)行過(guò)程分析:

4.2 事件循環(huán)算法

1. 執(zhí)行同步代碼(調(diào)用棧)
2. 調(diào)用棧清空后,檢查微任務(wù)隊(duì)列
3. 執(zhí)行所有微任務(wù)(直到微任務(wù)隊(duì)列清空)
4. 必要時(shí)進(jìn)行UI渲染
5. 從宏任務(wù)隊(duì)列取一個(gè)任務(wù)執(zhí)行
6. 回到步驟2,循環(huán)...

5. 實(shí)際代碼演示

5.1 復(fù)雜示例分析

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

setTimeout(function() {
    console.log('timeout1'); // 5. 宏任務(wù)
    
    Promise.resolve().then(function() {
        console.log('promise1'); // 6. 微任務(wù)
    });
}, 0);

Promise.resolve().then(function() {
    console.log('promise2'); // 3. 微任務(wù)
    
    setTimeout(function() {
        console.log('timeout2'); // 7. 宏任務(wù)
    }, 0);
});

console.log('end'); // 2. 同步

// 輸出順序:
// start
// end
// promise2
// timeout1
// promise1
// timeout2

5.2 嵌套任務(wù)執(zhí)行順序

console.log('1. 開(kāi)始');

setTimeout(() => {
    console.log('2. 外層宏任務(wù)');
    
    Promise.resolve().then(() => {
        console.log('3. 外層微任務(wù)');
    });
    
    setTimeout(() => {
        console.log('4. 內(nèi)層宏任務(wù)');
    }, 0);
}, 0);

Promise.resolve().then(() => {
    console.log('5. 外層微任務(wù)');
    
    setTimeout(() => {
        console.log('6. 微任務(wù)中的宏任務(wù)');
    }, 0);
});

console.log('7. 結(jié)束');

// 執(zhí)行順序分析:
// 1 → 7 → 5 → 2 → 3 → 6 → 4

6. 瀏覽器 vs Node.js 事件循環(huán)差異

6.1 瀏覽器事件循環(huán)

// 瀏覽器中的階段:
// 1. 執(zhí)行同步代碼
// 2. 執(zhí)行微任務(wù)
// 3. UI渲染(如果需要)
// 4. 執(zhí)行宏任務(wù)

console.log('腳本開(kāi)始');

// 宏任務(wù)
setTimeout(() => console.log('計(jì)時(shí)器'));

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

// 動(dòng)畫(huà)幀回調(diào)(在渲染前執(zhí)行)
requestAnimationFrame(() => console.log('RAF'));

console.log('腳本結(jié)束');

6.2 Node.js 事件循環(huán)

// Node.js 有更復(fù)雜的階段:
// timers → pending callbacks → idle, prepare → poll → check → close callbacks

console.log('開(kāi)始');

setTimeout(() => console.log('timer1'), 0);
setImmediate(() => console.log('immediate'));

process.nextTick(() => console.log('nextTick'));

Promise.resolve().then(() => console.log('promise'));

console.log('結(jié)束');

// Node.js 輸出可能:
// 開(kāi)始 → 結(jié)束 → nextTick → promise → timer1 → immediate
// 或者:開(kāi)始 → 結(jié)束 → nextTick → promise → immediate → timer1

7. 常見(jiàn)面試題分析

7.1 經(jīng)典面試題

console.log('1');

setTimeout(() => {
    console.log('2');
    Promise.resolve().then(() => {
        console.log('3');
    });
}, 0);

new Promise((resolve) => {
    console.log('4');
    resolve();
}).then(() => {
    console.log('5');
});

console.log('6');

// 輸出順序:1 → 4 → 6 → 5 → 2 → 3

7.2 進(jìn)階面試題

async function async1() {
    console.log('async1 start');
    await async2();
    console.log('async1 end');
}

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

console.log('script start');

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

async1();

new Promise((resolve) => {
    console.log('promise1');
    resolve();
}).then(() => {
    console.log('promise2');
});

console.log('script end');

// 輸出順序:
// script start → async1 start → async2 → promise1 → script end
// → async1 end → promise2 → setTimeout

8. 性能優(yōu)化實(shí)踐

8.1 避免阻塞事件循環(huán)

// 錯(cuò)誤示范:同步耗時(shí)操作阻塞事件循環(huán)
function processLargeData(data) {
    // 這個(gè)函數(shù)執(zhí)行時(shí)間很長(zhǎng),會(huì)阻塞頁(yè)面
    for (let i = 0; i < 1000000; i++) {
        // 繁重的同步計(jì)算
    }
}

// 正確示范:將任務(wù)分解為異步執(zhí)行
async function processLargeDataAsync(data) {
    for (let i = 0; i < data.length; i += 1000) {
        const chunk = data.slice(i, i + 1000);
        
        // 使用 setTimeout 或 Promise 讓出控制權(quán)
        await new Promise(resolve => setTimeout(resolve, 0));
        processChunk(chunk);
    }
}

8.2 合理使用微任務(wù)和宏任務(wù)

// 緊急任務(wù)使用微任務(wù)
function urgentTask() {
    Promise.resolve().then(() => {
        // 需要立即執(zhí)行的任務(wù)
        updateUI();
    });
}

// 非緊急任務(wù)使用宏任務(wù)  
function nonUrgentTask() {
    setTimeout(() => {
        // 可以延遲執(zhí)行的任務(wù)
        logAnalytics();
    }, 0);
}

9. 調(diào)試技巧

9.1 查看任務(wù)隊(duì)列

// 添加調(diào)試信息
let microtaskCount = 0;
let macrotaskCount = 0;

// 包裝 Promise 來(lái)跟蹤微任務(wù)
const originalThen = Promise.prototype.then;
Promise.prototype.then = function(...args) {
    microtaskCount++;
    console.log(`微任務(wù)創(chuàng)建,總數(shù): ${microtaskCount}`);
    return originalThen.apply(this, args);
};

// 跟蹤宏任務(wù)
const originalSetTimeout = window.setTimeout;
window.setTimeout = function(...args) {
    macrotaskCount++;
    console.log(`宏任務(wù)創(chuàng)建,總數(shù): ${macrotaskCount}`);
    return originalSetTimeout.apply(this, args);
};

總結(jié)

事件循環(huán)要點(diǎn):

  1. 同步代碼優(yōu)先:先執(zhí)行完所有同步任務(wù)
  2. 微任務(wù)優(yōu)先:微任務(wù)在宏任務(wù)之前執(zhí)行
  3. 隊(duì)列清空:每個(gè)宏任務(wù)執(zhí)行后都會(huì)清空微任務(wù)隊(duì)列
  4. 循環(huán)不斷:事件循環(huán)持續(xù)檢查新任務(wù)

記憶口訣:

“同微宏,微先走,宏之后,微清空”

以上為個(gè)人經(jīng)驗(yàn),希望能給大家一個(gè)參考,也希望大家多多支持腳本之家。

相關(guān)文章

  • JavaScript數(shù)組排序的六種常見(jiàn)算法總結(jié)

    JavaScript數(shù)組排序的六種常見(jiàn)算法總結(jié)

    這篇文章主要給大家介紹了關(guān)于JavaScript數(shù)組排序的六種常見(jiàn)算法,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者使用JavaScript數(shù)組具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面來(lái)一起學(xué)習(xí)學(xué)習(xí)吧
    2020-08-08
  • js 如何刪除對(duì)象里的某個(gè)屬性

    js 如何刪除對(duì)象里的某個(gè)屬性

    這篇文章主要介紹了js 如何刪除對(duì)象里的某個(gè)屬性,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教
    2022-09-09
  • 使用console進(jìn)行性能測(cè)試

    使用console進(jìn)行性能測(cè)試

    各大瀏覽器內(nèi)置的開(kāi)發(fā)工具,都提供了一個(gè)console對(duì)象。它主要有兩個(gè)作用:顯示網(wǎng)頁(yè)代碼運(yùn)行時(shí)的錯(cuò)誤信息。提供了一個(gè)命令行接口,用來(lái)與網(wǎng)頁(yè)代碼互動(dòng)。下面我們就來(lái)詳細(xì)研究下如何使用console進(jìn)行性能測(cè)試。
    2015-04-04
  • 靈活使用數(shù)組制作圖片切換js實(shí)現(xiàn)

    靈活使用數(shù)組制作圖片切換js實(shí)現(xiàn)

    這篇文章主要介紹了靈活使用數(shù)組制作圖片切換效果,js實(shí)現(xiàn)圖片切換特效,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下
    2016-07-07
  • JavaScript對(duì)象與數(shù)組的幾種常見(jiàn)復(fù)制方法

    JavaScript對(duì)象與數(shù)組的幾種常見(jiàn)復(fù)制方法

    在 JavaScript 開(kāi)發(fā)中,對(duì)象和數(shù)組的復(fù)制是一個(gè)非常常見(jiàn)的操作,無(wú)論是函數(shù)參數(shù)傳遞、狀態(tài)管理,還是避免不必要的副作用,了解和掌握各種復(fù)制方式至關(guān)重要,我們將系統(tǒng)梳理 JavaScript 中的幾種常見(jiàn)復(fù)制方法,并對(duì)其應(yīng)用場(chǎng)景進(jìn)行詳細(xì)說(shuō)明,需要的朋友可以參考下
    2024-10-10
  • 收集的一些Array及String原型對(duì)象的擴(kuò)展實(shí)現(xiàn)代碼

    收集的一些Array及String原型對(duì)象的擴(kuò)展實(shí)現(xiàn)代碼

    收集的一些Array及String原型對(duì)象的擴(kuò)展實(shí)現(xiàn)代碼,學(xué)習(xí)js的朋友可以參考下。并可以自定義的對(duì)字符串與array數(shù)據(jù),進(jìn)行擴(kuò)展。
    2010-12-12
  • javascript利用apply和arguments復(fù)用方法

    javascript利用apply和arguments復(fù)用方法

    這篇文章主要介紹了javascript利用apply和arguments復(fù)用方法,有需要的朋友可以參考一下
    2013-11-11
  • 基于Proxy的小程序狀態(tài)管理實(shí)現(xiàn)

    基于Proxy的小程序狀態(tài)管理實(shí)現(xiàn)

    這篇文章主要介紹了基于Proxy的小程序狀態(tài)管理實(shí)現(xiàn),小編覺(jué)得挺不錯(cuò)的,現(xiàn)在分享給大家,也給大家做個(gè)參考。一起跟隨小編過(guò)來(lái)看看吧
    2019-06-06
  • JS實(shí)現(xiàn)數(shù)組的增刪改查操作示例

    JS實(shí)現(xiàn)數(shù)組的增刪改查操作示例

    這篇文章主要介紹了JS實(shí)現(xiàn)數(shù)組的增刪改查操作,結(jié)合實(shí)例形式分析了javascript針對(duì)數(shù)組的追加、獲取、刪除、添加、修改等常見(jiàn)操作技巧與相關(guān)注意事項(xiàng),需要的朋友可以參考下
    2018-08-08
  • 詳解template標(biāo)簽用法(含vue中的用法總結(jié))

    詳解template標(biāo)簽用法(含vue中的用法總結(jié))

    這篇文章主要介紹了template標(biāo)簽用法(含vue中的用法總結(jié)),本文給大家介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友參考下吧
    2021-01-01

最新評(píng)論

成都市| 新郑市| 兴业县| 永胜县| 栖霞市| 江山市| 安丘市| 松江区| 嫩江县| 沂源县| 奉贤区| 台东市| 阿克陶县| 调兵山市| 晋州市| 罗甸县| 重庆市| 浮梁县| 阿图什市| 广州市| 綦江县| 南开区| 武陟县| 铜梁县| 广南县| 连城县| 子洲县| 汉中市| 安图县| 临武县| 綦江县| 秦皇岛市| 如东县| 平江县| 门头沟区| 阿瓦提县| 临猗县| 同江市| 鄂托克旗| 安平县| 桃园县|