1. 如何工作的?

事件循环又叫做消息循环,是浏览器主线程的工作方式,JS 是浏览器的语言。因为 JS 是单线程,这意味着它一次只能做一件事,为了解决代码阻塞的问题,出现了任务循环。每次循环去任务队列取任务到主线程进行执行。

  1. 执行同步代码:首先,整段 <script> 代码被当作第一个宏任务来执行。遇到同步代码,立即执行。
  2. 分发异步任务:在执行过程中,如果遇到微任务(如 Promise.then()),就将其放入微任务队列;如果遇到新的宏任务(如 setTimeout),就将其放入宏任务队列
  3. 清空微任务:当第一个宏任务(也就是初始的同步代码)执行完毕后,主线程会立即检查微任务队列,然后执行并清空所有的微任务。
  4. UI 渲染(浏览器环境):在微任务队列清空后,浏览器可能会进行一次页面渲染。
  5. 执行下一个宏任务:然后,事件循环会去宏任务队列中,只取出一个任务来执行。

重复循环:这个新的宏任务执行完毕后,再次重复第 3 步:清空所有微任务 -> (可能)UI渲染 -> 再去取下一个宏任务... 这个过程不断循环。

2. 举个例子

console.log('Start'); // 宏任务 (script)

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

Promise.resolve().then(() => {
  console.log('Promise 1'); // 微任务
});

setTimeout(() => {
  console.log('setTimeout 2'); // 宏任务
}, 0);

console.log('End'); // 宏任务 (script)

执行顺序分析:

第一次宏任务 (script):

  • console.log('Start'); 输出 Start
  • setTimeout 被添加到宏任务队列。
  • Promise.resolve().then() 被添加到微任务队列。
  • setTimeout 被添加到宏任务队列。
  • console.log('End'); 输出 End

清空微任务队列:

  • 执行 Promise.resolve().then(),输出 Promise 1

第一次宏任务 (setTimeout 1):

  • 执行第一个 setTimeout 的回调,输出 setTimeout 1
  • Promise.resolve().then() 被添加到微任务队列。

清空微任务队列:

  • 执行 Promise inside setTimeout,输出 Promise inside setTimeout

第二次宏任务 (setTimeout 2):

  • 执行第二个 setTimeout 的回调,输出 setTimeout 2
Start
End
Promise 1
setTimeout 1
Promise inside setTimeout
setTimeout 2

3. 为什么会有宏任务微任务之分?

其实是为了优先级考虑,如果说正在执行一个耗时较长的宏任务,突然来了一个 API 请求,那么这个这个数据可能要等很久才能显示,体验不是很好。

所以说有宏任务和微任务之分,是让紧急任务可以插队,在当前任务完成后立刻执行,应用响应更快。

Logo

有“AI”的1024 = 2048,欢迎大家加入2048 AI社区

更多推荐