• 作者:老汪软件技巧
  • 发表时间:2024-09-21 10:01
  • 浏览量:

JavaScript 的事件循环机制决定了代码的执行顺序,是理解异步编程的核心。为了帮助大家更全面地掌握事件循环,本篇文章将通过一个综合案例深入解析事件循环中同步任务、异步任务、宏任务与微任务的执行顺序。

1. JavaScript 事件循环机制概述

js是单线程的,一次只能执行一个任务,为了避免阻塞主线程,js通过事件循环机制来处理同步和异步代码。

宏任务与微任务:异步任务可以分为宏任务和微任务,每次执行完一个宏任务之后,都会立即执行所有的微任务,然后再执行下一个宏任务,这样循环执行任务代码,直到循环完所有代码。

宏任务:整体代码块(如 script 标签中的代码)、setTimeout、setInterval;

微任务:包括 Promise 的回调函数、process.nextTick(Node.js)等

2. 综合案例:事件循环中的执行顺序

我们将通过一个综合案例来解释事件循环的执行顺序和宏任务、微任务的优先级。


console.log("script start");
setTimeout(() => {
  console.log("setTimeout 1");
}, 0);
Promise.resolve().then(() => {
  console.log("promise 1");
}).then(() => {
  console.log("promise 2");
});
setTimeout(() => {
  console.log("setTimeout 2");
}, 0);
async function async1() {
  console.log("async1 start");
  await async2();
  console.log("async1 end");
}
async function async2() {
  console.log("async2");
}
async1();
new Promise((resolve) => {
  console.log("promise 3");
  resolve();
}).then(() => {
  console.log("promise 4");
});
console.log("script end");

2.1 执行顺序分析

主线程执行同步代码:

执行微任务队列:

执行宏任务队列:

2.2 综合案例的执行结果

最终输出顺序:

arduino
复制代码
script start
async1 start
async2
promise 3
script end
promise 1
promise 2
async1 end
promise 4
setTimeout 1
setTimeout 2

2.3 详细执行流程解析

同步代码执行阶段:

微任务队列执行阶段:

宏任务队列执行阶段:

3. 深入理解 async/await 与事件循环

async/await 是 Promise 的语法糖,它会在 await 语句后将后续代码放入微任务队列中。

3.1 案例:async/await 与事件循环的交互

javascript
复制代码
async function foo() {
  console.log('foo start');
  await bar();
  console.log('foo end');
}
async function bar() {
  console.log('bar');
}
console.log('script start');
foo();
console.log('script end');

执行顺序:

console.log('script start'):输出 script start。调用 foo(),输出 foo start,然后 await bar(),调用 bar(),输出 bar。foo 函数暂停,await 之后的代码被放入微任务队列中。console.log('script end'):输出 script end。主线程空闲,开始执行微任务队列,输出 foo end。

最终输出:

sql
复制代码
script start
foo start
bar
script end
foo end

4. 常见事件循环陷阱4.1 setTimeout 延迟的误解

javascript
复制代码
setTimeout(() => {
  console.log('setTimeout 1');
}, 0);
for (let i = 0; i < 1000000000; i++) {} // 模拟长时间的同步操作
setTimeout(() => {
  console.log('setTimeout 2');
}, 0);

输出顺序:

arduino
复制代码
setTimeout 1
setTimeout 2

解释:尽管两个 setTimeout 都设置了 0ms 延迟,但由于主线程被长时间同步任务阻塞,定时器的回调会被延迟执行。

4.2 Promise 链的递归执行

javascript
复制代码
Promise.resolve().then(() => {
  console.log("Promise 1");
  Promise.resolve().then(() => {
    console.log("Promise 2");
  });
});

输出顺序:

javascript
复制代码
Promise 1
Promise 2

解释:Promise 1 的回调进入微任务队列并被执行,在 Promise 1 回调中创建的 Promise 2 的回调会在当前微任务队列执行完毕后执行。

5. 性能优化建议避免主线程阻塞:将复杂的计算或大数据处理放在 Web Worker 或 Node.js 的 Worker Threads 中处理。合理使用微任务与宏任务:通过微任务处理优先级较高的任务,如使用 Promise 和 queueMicrotask。分解复杂的异步任务:将复杂的异步任务分解成多个小任务,避免长时间阻塞主线程。6. 结语

通过本文的综合案例解析,我们深入了解了 JavaScript 事件循环机制的执行顺序、宏任务与微任务的区别,以及 async/await 与事件循环的交互原理。理解事件循环机制能够帮助我们更好地编写高效、性能优良的 JavaScript 代码。希望本文能够为你在学习和工作中提供有益的参考!


上一条查看详情 +解析 pcap 文件的 Python 库
下一条 查看详情 +没有了