事件循环(Event Loop)是 JavaScript 运行时环境中负责管理异步任务执行的一种机制。在浏览器中, 事件循环是由浏览器的 JavaScript 引擎(如 V8 引擎)负责实现;在 Node.js 等环境中,也有自己的事件循环实现。 简单来说,事件循环就是轮询任务队列,执行任务,休眠的无限循环。
原因很简单,JavaScript 是单线程语言,单线程意味着同一时间只能执行一个任务,为了能有效处理异步任务,而不会阻塞主线程,就需要并发处理, 而事件循环就是并发的一种形式。这对于交互式的 Web 应用和处理网络请求等操作至关重要。
任务队列是事件循环的核心组成部分,用于管理和调度异步任务的执行顺序。当任务到来时,JavaScript 引擎可能处于忙碌状态,那么任务会被排入队列里。
队列中的任务遵循 “ 先进先出 ” 的原则。当浏览器引擎执行完 script 后,它会处理 setTimeout 事件,然后处理 I/O 事件,以此类推。
Tips: 在执行任务时不会进行渲染操作,仅在任务完成后才会进行渲染; 任务花费的时间过长,浏览器将无法执行其他任务。会抛出一个 “页面未响应” 的警报。这种情况常发生在有大量复杂的计算或死循环的程序错误时。
因为队列 “先进先出” 的原则,所以引入微任务队列来执行高优先级的任务。通过微任务可以让开发者更精确地控制异步操作的执行顺序和时机。
在每个宏任务执行之后,引擎会执行微任务队列中的 所有 任务,然后再执行其他宏任务。
Example:
setTimeout(() => console.log(1));
Promise.resolve().then(() => console.log(2));
console.log(3);
// 输出结果:3 2 1
引入微任务之后,事件循环算法优化为:
以上步骤非完整步骤,完整事件循环可以了解: 事件循环
微任务会在执行任何其他事件处理,或渲染,或执行任何其他宏任务之前完成。这确保了微任务之间的应用程序环境基本相同(没有鼠标坐标更改, 没有新的网络数据等)。
常见的宏任务:
<script>
常见的微任务:
then()
) setTimeout(fn, 0)
函数将渲染操作分批加入宏任务,
每次渲染 100 条数据。
let docBody = document.getElementById("body");
function loop(nums) {
if (nums <= 0) return;
setTimeout(() => {
for (let i = 0; i < 100; i++) {
let div = document.createElement("div");
div.innerHTML = i;
docBody.appendChild(div);
}
loop(nums - 100);
}, 0);
}
loop(100000);
new Promise(resolve => {
console.log(1);
resolve();
}).then(() => {
console.log(2);
})
console.log(3);
输出结果:1 3 2
Promise 本身是同步代码,then()
回调才属于微任务。
console.log(1);
async function async1() {
await async2(); // 立即执行
console.log(2); // 被加入微队列
await async3(); // 被加入微队列
console.log(3); // 被加入微队列
}
async function async2() {
console.log(4);
}
async function async3() {
console.log(5);
}
async1();
console.log(6);
输出结果:1 4 6 2 5 3
console.log(1);
setTimeout(() => console.log(2));
Promise.resolve().then(() => console.log(3));
Promise.resolve().then(() => setTimeout(() => console.log(4)));
Promise.resolve().then(() => console.log(5));
setTimeout(() => console.log(6));
console.log(7);
输出结果:1 7 3 5 2 6 4
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
console.log(1);
async function async1() {
await async2();
console.log(2);
await async3();
console.log(3);
}
async function async2() {
console.log(4);
}
async function async3() {
console.log(5);
}
async1();
setTimeout(() => console.log(6), 0);
new Promise(resolve => {
console.log(7);
resolve();
}).then(() => console.log(8)).then(() => console.log(9));
输出结果 1 4 7 2 5 8 3 9 6