前端面试常问到的:js中事件循环机制是什么(EventLoop)?

145 阅读4分钟

前端时间去面试 面试官问我对EventLoop了解吗?

我猛的想不起来 后来面试官跟我说就是事件循环机制,然后我才想起来

下面我对这次面试问到的事件循环机制(EventLoop)展开说明一下我的理解 以及面试官问的一些实践性问题 (大佬轻喷)

JavaScript中的事件循环(event loop)是一种机制,用于处理异步操作和事件处理。它是JavaScript单线程执行模型的核心部分,允许同时处理多个任务而不会阻塞主线程。

事件循环的基本原理如下:

  1. JavaScript代码执行开始时,会先处理同步任务(即主线程中的任务),按照顺序执行。
  2. 当遇到异步任务时,例如定时器、网络请求、事件监听等,它们会被放入事件队列(event queue)中,等待执行。
  3. 一旦主线程中的同步任务执行完毕,事件循环会检查事件队列,如果队列中存在任务,就会将队列中的第一个任务取出,并放入主线程中执行。
  4. 异步任务在执行时,如果遇到回调函数或者Promise的resolve/reject操作,这些操作也会被放入事件队列中,等待执行。
  5. 当主线程中的任务执行完毕后,会再次检查事件队列,如果队列中有任务,就继续执行。这样循环往复,不断处理事件队列中的任务。

这种机制保证了JavaScript的单线程模型,同时允许处理异步操作,避免了阻塞主线程。通过事件循环,JavaScript可以实现诸如定时器、AJAX请求、事件处理等功能,并提供了一种简单而高效的方式处理异步编程。

面试当中面试官经常会问到: 请解释JavaScript中的事件循环机制,并描述事件循环中宏任务(macrotask)和微任务(microtask)之间的区别。 这个题目主要考察面试者对JavaScript中事件循环机制的理解。通过回答这个问题,面试者需要清楚地解释事件循环的基本原理和执行过程,并能够准确描述宏任务和微任务的区别及其执行时机。 回答:事件循环是JavaScript中用于处理异步任务和事件的机制。它确保了JavaScript的单线程执行模型,同时处理异步操作而不会阻塞主线程。

在事件循环中,任务被分为两类:宏任务和微任务。宏任务代表的是较大的任务单元,例如setTimeout、setInterval、I/O操作等。而微任务则是较小的任务单元,例如Promise的回调函数、MutationObserver等。

事件循环的执行过程如下:

  1. 执行当前的宏任务。
  2. 检查微任务队列,如果存在微任务,依次执行所有微任务,直到微任务队列为空。
  3. 更新渲染(如果需要)。
  4. 从宏任务队列中取出下一个宏任务,如果队列为空,回到步骤2;否则,执行宏任务并回到步骤2。

宏任务和微任务之间的区别在于它们的执行时机和优先级。当主线程执行完一个宏任务后,会检查微任务队列,并在执行下一个宏任务之前清空微任务队列。换句话说,微任务的执行发生在宏任务之间。

微任务具有更高的优先级,它们会在下一个宏任务之前执行完毕。这意味着,如果在当前宏任务中产生了微任务,那么这些微任务会在下一个宏任务之前执行,确保了微任务的及时响应性。

总结起来,事件循环机制通过交替执行宏任务和微任务,实现了JavaScript的异步编程和事件处理能力。

下面说一下面试官问到的实践面试题:

console.log('1');

setTimeout(function() {
  console.log('2');
  Promise.resolve().then(function() {
    console.log('3');
  });
});

Promise.resolve().then(function() {
  console.log('4');
  setTimeout(function() {
    console.log('5');
  });
});

console.log('6');

让我逐行解释代码的执行过程:

  1. 首先,执行同步任务,输出1。
  2. 然后,遇到第一个setTimeout函数,将其作为宏任务添加到宏任务队列中。由于延迟时间为0,所以这个宏任务会尽快执行。
  3. 继续执行同步任务,输出6。
  4. 遇到第一个Promise.resolve().then,将其注册的回调函数添加到微任务队列中,等待执行。
  5. 执行第二个Promise.resolve().then,将其注册的回调函数添加到微任务队列中,等待执行。
  6. 执行微任务队列中的第一个任务,输出4。
  7. 继续执行同步任务,遇到第二个setTimeout函数,将其作为宏任务添加到宏任务队列中。
  8. 此时,同步任务执行完毕,开始执行微任务队列中的下一个任务。
  9. 执行微任务队列中的第二个任务,输出2。
  10. 执行微任务队列中的第三个任务,输出3。
  11. 最后,执行宏任务队列中的第二个任务,输出5。

综上所述,代码的执行顺序是1、6、4、2、3、5。