# 1. 什么是事件循环机制?

JS 中的事件循环(Event Loop)是一种用于管理和调度异步任务执行的机制。它使得 JS 可以处理异步操作,如定时器、事件处理、网络请求等,而不会阻塞主线程的执行。

# 2. 说一下什么是宏任务微任务,为什么要定义这两种任务类型?

宏任务(macro tasks)和微任务(micro tasks)是 JavaScript 引擎中用于管理异步任务执行顺序的两种任务类型。

宏任务(Macro tasks): 是指那些需要在主线程中执行的任务,它们包括但不限于:

  • 定时器任务(Timers):通过 setTimeout、setInterval 创建的任务。
  • I/O 操作任务(I/O operations):例如文件读写、网络请求等异步操作。
  • 渲染事件(UI Rendering):处理用户交互事件(例如鼠标点击、键盘事件等)的任务。
  • 事件处理器任务(Event handlers):事件监听器、事件回调等。

微任务(Micro tasks): 是指在当前任务执行完成后立即执行的任务,它们通常包括:

  • Promise 回调(Promise callbacks):使用 Promise 对象的 then 方法添加的回调函数。
  • MutationObserver 回调:通过 MutationObserver 观察 DOM 变化并触发的任务。
  • process.nextTick(在 Node.js 环境中):在事件循环的当前回合结束时执行的任务。

宏任务和微任务的引入使得 JavaScript 引擎能够更好地处理异步任务。

宏任务用于表示一组相对较大的任务单元,而微任务用于表示一组相对较小、优先级较高的任务单元。

通过微任务,我们可以在宏任务执行完成后立即执行一些重要的任务,如更新 UI、处理 Promise 的回调等,以提高应用的响应速度和用户体验。

# 3. 例题:

async function async1() {
    console.log('async1 start');
    await async2();
    console.log('async1 end');
  }
  async function async2() {
    console.log('async2');
  }
  console.log('script start');
  setTimeout(function () {
    console.log('setTimeout');
  }, 0);
  async1();
  new Promise(function (resolve) {
    console.log('promise1');
    resolve();
  }).then(function () {
    console.log('promise2');
  });
  console.log('script end');
答案:
script start
async1 start
async2
promise1
script end
async1 end
promise2
setTimeout

过程:

# 1. 同步任务(同步代码执行)

首先,所有同步任务按顺序执行。

  • 第 1 步console.log('script start');
    • 输出: script start
  • 第 2 步:调用 async1 函数。
    • 进入 async1 函数, console.log('async1 start');
    • 输出: async1 start
    • 遇到 await async2()async2 被调用。
  • 第 3 步:进入 async2 函数, console.log('async2');
    • 输出: async2
    • async2 函数执行结束,回到 async1
  • 第 4 步await 表达式遇到一个 Promiseasync1 函数暂停并将控制权交回主线程,继续执行后面的同步代码。
  • 第 5 步:创建一个 Promise ,立即执行其回调函数, console.log('promise1');
    • 输出: promise1
  • 第 6 步Promise 的回调函数执行完毕,注册了一个 .then 回调到微任务队列。
  • 第 7 步console.log('script end');
    • 输出: script end

# 2. 微任务(Microtasks)队列处理

主线程上的同步任务执行完毕,开始处理微任务队列。

  • 第 8 步async1await 表达式完成(此时它已经变成一个 Promise ),恢复执行 async1 函数的后续代码。
    • 执行 console.log('async1 end');
    • 输出: async1 end
  • 第 9 步Promise.then 回调执行。
    • 执行 console.log('promise2');
    • 输出: promise2

# 3. 宏任务(Macrotasks)队列处理

微任务队列清空后,开始处理宏任务队列中的任务。

  • 第 10 步

    :处理

    setTimeout
    

    的回调函数。

    • 执行 console.log('setTimeout');
    • 输出: setTimeout
const promise = new Promise((resolve, reject) => {
    console.log(1);
  setTimeout(() => {
    console.log("6");
    setTimeout(()=>{console.log(8);},0)
    resolve('success')
    console.log(7);
  }, 0);
  console.log(2);
});
setTimeout(()=>{
    console.log(5);
},0)
promise.then((res)=>{
    console.log(res);
})
console.log(4);
答案:
1
2
4
5
6
7
success
8

# 1. 执行宏任务( Macrotask

  • 遇到第一个宏任务( setTimeout 回调),执行回调函数:
    • 输出: 5
  • 执行下一个宏任务( Promise 内的 setTimeout 回调),执行回调函数:
    • 输出: 6
    • 注册一个新的 setTimeout ,将其加入到宏任务队列中。
    • 调用 resolve('success') ,将 then 回调加入到微任务队列中。
    • 输出: 7

# 2. 执行微任务( Microtask

  • 执行微任务队列中的回调函数:

    • 执行

      promise.then
      

      的回调,输出

      res
      

      的值:

      success
      
      • 输出: success

# 3. 执行下一个宏任务( Macrotask

  • 执行最后一个

    setTimeout
    

    回调:

    • 输出: 8