JavaScript事件循環(huán)深入講解及常見誤區(qū)
一、JS 的單線程模型與異步機(jī)制
JS 是一種單線程語言,這意味著它只有一個(gè)主線程(執(zhí)行棧)來處理所有任務(wù)。這種設(shè)計(jì)避免了多線程環(huán)境中的復(fù)雜同步問題,但也帶來了一個(gè)挑戰(zhàn):如何防止長時(shí)間運(yùn)行的代碼阻塞整個(gè)程序?
解決方案是將代碼分為:
- 同步代碼:由 JS 引擎直接執(zhí)行
- 異步代碼:交給宿主環(huán)境(瀏覽器/Node.js)處理
二、事件循環(huán)的核心組件
1. 執(zhí)行棧(Call Stack)
- 用于存儲同步任務(wù)的執(zhí)行上下文
- 遵循后進(jìn)先出(LIFO)原則
- 當(dāng)函數(shù)執(zhí)行時(shí)會被推入棧頂,執(zhí)行完畢后彈出
2. 任務(wù)隊(duì)列(Task Queue)
- 宏任務(wù)隊(duì)列(Macrotask Queue)
- 微任務(wù)隊(duì)列(Microtask Queue)
3. Web APIs
- 瀏覽器提供的異步API(setTimeout、DOM事件等)
- Node.js 中的 I/O 操作等
三、事件循環(huán)的執(zhí)行流程
執(zhí)行同步代碼:執(zhí)行棧中的任務(wù)依次執(zhí)行
處理微任務(wù):
- 執(zhí)行棧清空后,立即執(zhí)行所有微任務(wù)
- 微任務(wù)執(zhí)行期間產(chǎn)生的新微任務(wù)會繼續(xù)執(zhí)行
渲染更新(瀏覽器環(huán)境)
取一個(gè)宏任務(wù)執(zhí)行
重復(fù)循環(huán)

四、任務(wù)類型詳解
1. 宏任務(wù)(Macrotask)
| 來源 | 示例 |
|---|---|
| setTimeout/setInterval | setTimeout(fn, 0) |
| I/O 操作 | 文件讀寫、網(wǎng)絡(luò)請求 |
| UI 渲染 | (瀏覽器) |
| 事件回調(diào) | click, scroll 等 |
| setImmediate | (Node.js 特有) |
特點(diǎn):
- 每次事件循環(huán)只執(zhí)行一個(gè)宏任務(wù)
- 優(yōu)先級低于微任務(wù)
2. 微任務(wù)(Microtask)
| 來源 | 示例 |
|---|---|
| Promise | .then()/.catch() |
| MutationObserver | DOM 變更觀察 |
| process.nextTick | (Node.js 特有,優(yōu)先級最高) |
特點(diǎn):
- 在當(dāng)前宏任務(wù)結(jié)束后立即執(zhí)行
- 會清空整個(gè)微任務(wù)隊(duì)列
- 優(yōu)先級高于宏任務(wù)
五、經(jīng)典執(zhí)行順序示例
console.log('1. 同步代碼開始');
setTimeout(() => {
console.log('6. 宏任務(wù)1 - setTimeout');
Promise.resolve().then(() => {
console.log('7. 微任務(wù)3 - Promise');
});
}, 0);
Promise.resolve().then(() => {
console.log('3. 微任務(wù)1 - Promise');
return Promise.resolve();
}).then(() => {
console.log('4. 微任務(wù)2 - Promise');
});
console.log('2. 同步代碼結(jié)束');
// 輸出順序:
// 1. 同步代碼開始
// 2. 同步代碼結(jié)束
// 3. 微任務(wù)1 - Promise
// 4. 微任務(wù)2 - Promise
// 6. 宏任務(wù)1 - setTimeout
// 7. 微任務(wù)3 - Promise
六、實(shí)際應(yīng)用場景
1. 定時(shí)任務(wù)控制
// 動畫幀控制
function animate() {
// 動畫邏輯
requestAnimationFrame(animate); // 比setTimeout更適合動畫
}
animate();
// 輪詢檢查
function poll() {
fetch('/api/status')
.then(checkStatus)
.then(() => setTimeout(poll, 5000));
}
2. Promise 異步流程
function loadData() {
return fetch('/api/data')
.then(response => response.json())
.then(data => {
// 處理數(shù)據(jù)
return processData(data);
})
.catch(error => {
// 錯(cuò)誤處理
console.error(error);
});
}
3. DOM 事件優(yōu)化
// 防抖處理高頻事件
function debounce(fn, delay) {
let timer;
return function() {
clearTimeout(timer);
timer = setTimeout(() => fn.apply(this, arguments), delay);
};
}
window.addEventListener('scroll', debounce(() => {
// 處理滾動邏輯
}, 100));
七、常見誤區(qū)與最佳實(shí)踐
1. 不要阻塞事件循環(huán)
// 錯(cuò)誤示范:同步計(jì)算阻塞UI
function heavyCalc() {
let result = 0;
for (let i = 0; i < 1000000000; i++) {
result += Math.sqrt(i);
}
return result;
}
// 正確做法:分片處理
async function chunkedHeavyCalc() {
let result = 0;
for (let i = 0; i < 100000000; i += 100000) {
result += await chunkCalc(i, Math.min(i + 100000, 100000000));
// 允許瀏覽器渲染
await new Promise(resolve => requestAnimationFrame(resolve));
}
return result;
}
2. 微任務(wù)嵌套陷阱
// 可能導(dǎo)致無限循環(huán)
function microtaskLoop() {
Promise.resolve().then(microtaskLoop);
}
// microtaskLoop(); // 不要這樣做!
3. 合理使用任務(wù)優(yōu)先級
// 需要立即執(zhí)行的任務(wù)使用微任務(wù)
function urgentTask(callback) {
Promise.resolve().then(callback);
}
// 不緊急的任務(wù)使用宏任務(wù)
function backgroundTask(callback) {
setTimeout(callback, 0);
}
總結(jié)
到此這篇關(guān)于JavaScript事件循環(huán)深入講解及常見誤區(qū)的文章就介紹到這了,更多相關(guān)JS事件循環(huán)詳解內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
原生js實(shí)現(xiàn)移動端Touch輪播圖的方法步驟
這篇文章主要介紹了原生js實(shí)現(xiàn)移動端Touch輪播圖的方法步驟,touch輪播圖其實(shí)就是通過手指的滑動,來左右切換輪播圖,touch輪播圖其實(shí)就是通過手指的滑動,來左右切換輪播圖,2019-01-01
深入理解JavaScript系列(6) 強(qiáng)大的原型和原型鏈
JavaScript 不包含傳統(tǒng)的類繼承模型,而是使用 prototypal 原型模型2012-01-01
JavaScript類型系統(tǒng)之正則表達(dá)式
正則又叫規(guī)則或模式,是一個(gè)強(qiáng)大的字符串匹配工具。javascript通過RegExp類型來支持正則表達(dá)式,本文給大家介紹javascript類型系統(tǒng)之正則表達(dá)式,對js正則表達(dá)式相關(guān)知識感興趣的朋友一起學(xué)習(xí)吧2016-01-01
JSON.parse處理非標(biāo)準(zhǔn)Json數(shù)據(jù)出錯(cuò)的解決
這篇文章主要介紹了JSON.parse處理非標(biāo)準(zhǔn)Json數(shù)據(jù)出錯(cuò)的解決,具有很好的參考價(jià)值,希望對大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2022-09-09
js實(shí)現(xiàn)首屏延遲加載實(shí)現(xiàn)方法 js實(shí)現(xiàn)多屏單張圖片延遲加載效果
這篇文章主要介紹了js實(shí)現(xiàn)首屏延遲加載實(shí)現(xiàn)方法,以及js實(shí)現(xiàn)多屏單張圖片延遲加載效果,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2017-07-07
uniapp踩坑實(shí)戰(zhàn)之文件查找失敗:'uview-ui'?at?main.js解決辦法
這篇文章主要給大家介紹了關(guān)于uniapp踩坑實(shí)戰(zhàn)之文件查找失敗:'uview-ui'?at?main.js的解決辦法,文中通過代碼介紹的非常詳細(xì),需要的朋友可以參考下2023-12-12

