事件循环
事件循环简介
事件循环(Event Loop)是JavaScript实现异步编程的核心机制,负责协调执行栈、任务队列和微任务队列的执行。
JavaScript执行环境
1. 执行栈
执行栈是JavaScript代码执行的地方,采用后进先出(LIFO)的数据结构。
javascript
console.log('1');
function foo() {
console.log('2');
}
function bar() {
console.log('3');
foo();
}
bar();
console.log('4');
// 输出顺序: 1, 3, 2, 42. 任务队列
任务队列分为宏任务队列和微任务队列。
宏任务(Macro Task)
javascript
// setTimeout
setTimeout(() => {
console.log('setTimeout');
}, 0);
// setInterval
setInterval(() => {
console.log('setInterval');
}, 1000);
// I/O
fs.readFile('file.txt', (err, data) => {
console.log('I/O');
});
// UI渲染
requestAnimationFrame(() => {
console.log('requestAnimationFrame');
});微任务(Micro Task)
javascript
// Promise.then()
Promise.resolve().then(() => {
console.log('Promise.then');
});
// MutationObserver
const observer = new MutationObserver(() => {
console.log('MutationObserver');
});
observer.observe(document.body, { childList: true });
// queueMicrotask
queueMicrotask(() => {
console.log('queueMicrotask');
});事件循环流程
完整流程
1. 执行同步代码(执行栈)
2. 执行所有微任务
3. 执行一个宏任务
4. 执行所有微任务
5. 重复3-4步骤示例
javascript
console.log('1');
setTimeout(() => {
console.log('2');
}, 0);
Promise.resolve().then(() => {
console.log('3');
});
console.log('4');
// 输出顺序: 1, 4, 3, 2详细执行过程
javascript
console.log('1'); // 1. 同步代码
setTimeout(() => {
console.log('2'); // 3. 宏任务
}, 0);
Promise.resolve().then(() => {
console.log('3'); // 2. 微任务
});
console.log('4'); // 1. 同步代码
// 执行顺序:
// 1. 执行同步代码: 输出 1, 4
// 2. 执行微任务: 输出 3
// 3. 执行宏任务: 输出 2复杂示例
示例1
javascript
console.log('1');
setTimeout(() => {
console.log('2');
Promise.resolve().then(() => {
console.log('3');
});
}, 0);
Promise.resolve().then(() => {
console.log('4');
setTimeout(() => {
console.log('5');
}, 0);
});
console.log('6');
// 输出顺序: 1, 6, 4, 2, 3, 5示例2
javascript
console.log('1');
setTimeout(() => {
console.log('2');
}, 0);
Promise.resolve().then(() => {
console.log('3');
Promise.resolve().then(() => {
console.log('4');
});
});
Promise.resolve().then(() => {
console.log('5');
});
console.log('6');
// 输出顺序: 1, 6, 3, 5, 4, 2示例3
javascript
console.log('1');
setTimeout(() => {
console.log('2');
setTimeout(() => {
console.log('3');
}, 0);
}, 0);
setTimeout(() => {
console.log('4');
}, 0);
console.log('5');
// 输出顺序: 1, 5, 2, 4, 3async/await
async/await与事件循环
javascript
async function foo() {
console.log('1');
await Promise.resolve();
console.log('2');
}
async function bar() {
console.log('3');
await Promise.resolve();
console.log('4');
}
console.log('5');
foo();
bar();
console.log('6');
// 输出顺序: 5, 1, 3, 6, 2, 4async/await原理
javascript
async function foo() {
console.log('1');
await Promise.resolve();
console.log('2');
}
// 等价于
function foo() {
console.log('1');
return Promise.resolve().then(() => {
console.log('2');
});
}Promise
Promise与事件循环
javascript
console.log('1');
Promise.resolve().then(() => {
console.log('2');
Promise.resolve().then(() => {
console.log('3');
});
});
Promise.resolve().then(() => {
console.log('4');
});
console.log('5');
// 输出顺序: 1, 5, 2, 4, 3Promise链式调用
javascript
Promise.resolve()
.then(() => {
console.log('1');
return Promise.resolve();
})
.then(() => {
console.log('2');
});
// 输出顺序: 1, 2requestAnimationFrame
requestAnimationFrame与事件循环
javascript
console.log('1');
requestAnimationFrame(() => {
console.log('2');
});
setTimeout(() => {
console.log('3');
}, 0);
console.log('4');
// 输出顺序: 1, 4, 3, 2requestAnimationFrame特点
- 在浏览器重绘之前执行
- 与屏幕刷新率同步(通常60fps)
- 适合动画效果
javascript
function animate() {
requestAnimationFrame(animate);
console.log('动画帧');
}
animate();Node.js事件循环
Node.js事件循环阶段
1. Timers: setTimeout、setInterval
2. Pending callbacks: I/O回调
3. Idle, prepare: 内部使用
4. Poll: 获取新的I/O事件
5. Check: setImmediate
6. Close callbacks: 关闭回调Node.js示例
javascript
console.log('1');
setTimeout(() => {
console.log('2');
}, 0);
setImmediate(() => {
console.log('3');
});
console.log('4');
// 输出顺序: 1, 4, 2, 3 或 1, 4, 3, 2Node.js与浏览器差异
javascript
// 浏览器
console.log('1');
setTimeout(() => {
console.log('2');
}, 0);
Promise.resolve().then(() => {
console.log('3');
});
console.log('4');
// 输出顺序: 1, 4, 3, 2
// Node.js
console.log('1');
setTimeout(() => {
console.log('2');
}, 0);
Promise.resolve().then(() => {
console.log('3');
});
console.log('4');
// 输出顺序: 1, 4, 3, 2面试常见问题
1. 什么是事件循环?
事件循环是JavaScript实现异步编程的核心机制,负责协调执行栈、任务队列和微任务队列的执行。
2. 宏任务和微任务的区别?
- 宏任务: setTimeout、setInterval、I/O、UI渲染
- 微任务: Promise.then、MutationObserver、queueMicrotask
3. 事件循环的执行顺序?
1. 执行同步代码(执行栈)
2. 执行所有微任务
3. 执行一个宏任务
4. 执行所有微任务
5. 重复3-4步骤4. async/await的执行顺序?
javascript
async function foo() {
console.log('1');
await Promise.resolve();
console.log('2');
}
console.log('3');
foo();
console.log('4');
// 输出顺序: 3, 1, 4, 25. requestAnimationFrame的特点?
- 在浏览器重绘之前执行
- 与屏幕刷新率同步(通常60fps)
- 适合动画效果
通过理解事件循环的工作原理,可以更好地理解JavaScript的异步编程和性能优化。