MST

星途 面试题库

面试题:JavaScript Promise 的底层机制与性能优化

深入探讨 JavaScript Promise 的工作原理,包括微任务队列在 Promise 执行过程中的作用。如果在一个高并发场景下,有大量的 Promise 任务,如何优化 Promise 的使用以提高性能?请结合事件循环机制详细说明。
41.6万 热度难度
编程语言JavaScript

知识考点

AI 面试

面试题答案

一键面试

JavaScript Promise 工作原理

  1. 状态管理:Promise 有三种状态:pending(进行中)、fulfilled(已成功)和 rejected(已失败)。初始状态为 pending。一旦从 pending 状态转换到 fulfilledrejected 状态,就不会再改变。
  2. 构造函数与执行:通过 new Promise((resolve, reject) => { /* 执行器函数 */ }) 创建 Promise。执行器函数会立即同步执行,其中 resolve 用于将 Promise 状态变为 fulfilledreject 用于将状态变为 rejected
  3. 链式调用:Promise 的 then 方法返回一个新的 Promise,这使得可以进行链式调用。then 方法接受两个回调函数,第一个处理 fulfilled 状态,第二个处理 rejected 状态。如果前一个 Promise 成功,会调用下一个 then 的第一个回调;如果失败,会调用第二个回调。

微任务队列在 Promise 执行过程中的作用

  1. 微任务概念:微任务是一种在当前任务执行结束后,下一个宏任务执行之前执行的任务。
  2. Promise 与微任务:当 Promise 的状态发生改变(从 pending 变为 fulfilledrejected)时,then 方法中的回调函数会被放入微任务队列。这意味着即使当前的同步代码还未执行完毕,只要当前调用栈清空,微任务队列中的任务就会被执行。例如:
console.log('start');
new Promise((resolve) => {
  console.log('promise executor');
  resolve();
}).then(() => {
  console.log('promise then');
});
console.log('end');
// 输出:start -> promise executor -> end -> promise then

在这个例子中,promise then 会在当前同步代码(console.log('end'))执行完毕后,下一个宏任务开始前执行,因为 then 回调被放入了微任务队列。

高并发场景下 Promise 使用的性能优化及与事件循环机制结合

  1. 事件循环机制:JavaScript 是单线程的,通过事件循环机制来处理异步任务。事件循环会不断检查调用栈是否为空,当调用栈为空时,会从宏任务队列中取出一个任务放入调用栈执行,执行完后再处理微任务队列中的所有任务,如此循环。宏任务包括 setTimeoutsetIntervalI/O 操作等,微任务包括 Promise.thenMutationObserver 等。
  2. 优化策略
    • 限制并发数量:使用 Promise.allSettled 结合 async/await 来控制并发数量。例如,可以创建一个函数来处理一组任务,每次只执行固定数量的任务,当有任务完成时,再启动新的任务。
async function runTasks(tasks, concurrency) {
  const results = [];
  const running = [];
  for (let i = 0; i < tasks.length; i++) {
    const task = tasks[i];
    const promise = Promise.resolve(task());
    results.push(promise);
    running.push(promise);
    if (running.length >= concurrency) {
      await Promise.race(running);
      running.splice(running.indexOf(await Promise.race(running)), 1);
    }
  }
  return Promise.allSettled(results);
}
- **避免不必要的 Promise 创建**:如果某些操作可以同步完成,就不要用 Promise 包装。
- **合理利用微任务队列**:因为微任务会在宏任务之前执行,所以在高并发场景下,如果微任务队列中的任务过多,可能会导致宏任务长时间得不到执行,造成页面卡顿等问题。要尽量控制微任务的数量和执行时间。可以将一些非紧急的任务放入宏任务队列(如使用 `setTimeout` 延迟执行)。