返回

从一道题目窥探事件循环机制

前端

从一道题目窥探事件循环机制

在学习JavaScript的过程中,我们经常会听到“事件循环”这个概念。它是一个非常重要的机制,决定了JavaScript代码的执行顺序。为了更好地理解事件循环,我们先来看一道题目:

在浏览器控制台依次输入以下代码,请写出代码的输出顺序:

console.log('1'); // 同步任务
setTimeout(() => {
  console.log('2'); // 宏任务
}, 0);
Promise.resolve().then(() => {
  console.log('3'); // 微任务
});

这道题目看似简单,但它却涵盖了事件循环机制的几个关键概念:执行栈、消息队列、事件队列、宏任务和微任务。

执行栈

执行栈是一个后进先出的栈,用于记录当前正在执行的函数。当一个函数被调用时,它会将该函数push到执行栈顶,当该函数执行完成后,它将栈顶函数pop弹出。

消息队列

消息队列是一个先进先出的队列,用于存储那些需要异步执行的任务,比如setTimeout的回调函数、Promise的then方法等。当执行栈为空时,系统会从消息队列中取出一个任务执行。

事件队列

事件队列是一个先进先出的队列,用于存储那些需要在页面上触发的事件,比如点击事件、鼠标移动事件等。当执行栈为空时,系统会从事件队列中取出一个事件执行。

宏任务

宏任务是指那些需要较长时间才能完成的任务,比如setTimeout的回调函数、setInterval的回调函数等。宏任务会先被放入消息队列中,当执行栈为空时,系统会从消息队列中取出一个宏任务执行。

微任务

微任务是指那些需要立即执行的任务,比如Promise的then方法、MutationObserver的回调函数等。微任务会先被放入消息队列中,当执行栈为空时,系统会从消息队列中取出所有微任务执行,然后再取出宏任务执行。

代码输出顺序

现在我们来分析一下这道题目的代码输出顺序:

  1. console.log('1'); // 同步任务
  2. Promise.resolve().then(() => {
    console.log('3'); // 微任务
    });
  3. setTimeout(() => {
    console.log('2'); // 宏任务
    }, 0);

首先,console.log('1')是一个同步任务,它会立即执行并输出“1”。

然后,Promise.resolve().then(() => {
console.log('3'); // 微任务
});是一个微任务,它会先被放入消息队列中。

最后,setTimeout(() => {
console.log('2'); // 宏任务
}, 0);是一个宏任务,它也会先被放入消息队列中。

当执行栈为空时,系统会先从消息队列中取出微任务执行,因此console.log('3')会输出“3”。

然后,系统会从消息队列中取出宏任务执行,因此setTimeout的回调函数会输出“2”。

所以,这道题目的代码输出顺序是:“1”、“3”、“2”。

总结

通过这道题目,我们对JavaScript中的事件循环机制有了一个基本的了解。事件循环机制是一个非常重要的概念,它决定了JavaScript代码的执行顺序。在实际开发中,我们经常会遇到与事件循环机制相关的问题,因此掌握事件循环机制的知识非常重要。