HOOOS

JavaScript事件循环:一次性把异步搞明白!

0 101 前端探索者 JavaScript事件循环异步编程
Apple

JavaScript 是一门单线程语言,这意味着它一次只能执行一个任务。 但是,我们经常需要在 JavaScript 中执行一些耗时的操作,例如网络请求、定时器等等。 如果这些操作同步执行,会导致页面卡顿,用户体验非常差。 这时候,就需要用到 JavaScript 的事件循环机制来处理异步操作。

什么是事件循环?

你可以把事件循环想象成一个永动机,它不断地从任务队列中取出任务并执行。 整个过程大致如下:

  1. 调用栈 (Call Stack): JavaScript 引擎首先会执行调用栈中的代码,也就是我们编写的同步代码。 栈就像一个叠盘子,后进先出。 当遇到函数调用时,就将函数压入栈中;函数执行完毕,就从栈中弹出。
  2. 任务队列 (Task Queue): 当遇到异步操作时,例如 setTimeoutPromise,它们会被交给浏览器或 Node.js 的 API 处理。 这些 API 完成后,会将对应的回调函数放入任务队列中。 队列就像排队,先进先出。
  3. 事件循环 (Event Loop): 事件循环会不断地检查调用栈是否为空。 如果为空,它就会从任务队列中取出一个任务放入调用栈中执行。 如此循环往复,直到所有任务都执行完毕。

宏任务和微任务

任务队列实际上分为两种:宏任务队列和微任务队列。

  • 宏任务 (Macro Task): 例如 setTimeoutsetIntervalscript (首次执行的 script 代码块)、用户交互事件、I/O 操作等。
  • 微任务 (Micro Task): 例如 Promise.thenMutationObserverprocess.nextTick (Node.js 环境) 等。

事件循环的执行顺序是:

  1. 执行一个宏任务 (通常是 script 代码块)。
  2. 检查是否存在微任务队列。 如果有,则执行所有微任务,直到微任务队列为空。
  3. 更新渲染。
  4. 重复以上步骤。

为什么要区分宏任务和微任务?

这样做是为了更好地控制任务的优先级。 微任务通常是一些需要在当前宏任务执行完毕后立即执行的任务,例如对 DOM 的修改。 将它们放在微任务队列中,可以确保在下一次渲染之前完成这些操作,避免页面出现闪烁等问题。

举个例子

让我们来看一个例子来理解事件循环:

console.log('script start');

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

Promise.resolve().then(function() {
  console.log('promise1');
}).then(function() {
  console.log('promise2');
});

console.log('script end');

这段代码的执行顺序是:

  1. script start
  2. script end
  3. promise1
  4. promise2
  5. setTimeout

解释:

  • 首先,执行 script 代码块,输出 script startscript end
  • 然后,setTimeout 被放入宏任务队列,Promise.resolve().then 被放入微任务队列。
  • script 代码块执行完毕后,事件循环检查到微任务队列不为空,执行所有微任务,输出 promise1promise2
  • 微任务队列为空后,事件循环取出一个宏任务 (也就是 setTimeout 的回调函数) 放入调用栈执行,输出 setTimeout

总结

JavaScript 的事件循环机制是理解异步编程的关键。 掌握事件循环、宏任务和微任务的概念,可以帮助我们编写更高效、更稳定的 JavaScript 代码。 希望这篇文章能帮助你更好地理解 JavaScript 事件循环! 理解透彻事件循环,以后再面对异步编程,就不会感觉晕头转向啦,哈哈!

点评评价

captcha
健康