MST

星途 面试题库

面试题:JavaScript 中 async/await 性能优化及微任务队列机制深入分析

在复杂的异步操作场景下,`async/await` 可能会因为大量的异步任务堆积而影响性能。请分析 `async/await` 与 JavaScript 微任务队列机制的关系,以及如何通过优化微任务队列的使用来提升 `async/await` 相关异步操作的性能,同时举例说明在实际项目中可能出现性能问题的场景及优化方案。
25.6万 热度难度
编程语言JavaScript

知识考点

AI 面试

面试题答案

一键面试

async/await 与 JavaScript 微任务队列机制的关系

  1. async/await 基于 Promiseasync 函数始终返回一个 Promise 对象。当 async 函数内部遇到 await 关键字时,它会暂停函数的执行,直到其等待的 Promise 被解决(resolved)或被拒绝(rejected)。
  2. 微任务队列:JavaScript 有一个事件循环机制,其中微任务队列是事件循环的一部分。Promise 的 then 回调会被放入微任务队列。当当前执行栈清空后,事件循环会优先处理微任务队列中的任务,然后再处理宏任务队列。
  3. async/await 与微任务队列的关联:当 await 一个已解决的 Promise 时,await 之后的代码会被包装成一个微任务并放入微任务队列。例如:
async function asyncFunction() {
    const result = await Promise.resolve(1);
    console.log(result); // 会在当前执行栈清空后,作为微任务执行
}
asyncFunction();

通过优化微任务队列使用提升 async/await 性能的方法

  1. 控制微任务数量:避免在短时间内产生过多的微任务。例如,不要在一个循环内同步创建大量 await 操作,因为每次 await 后的代码都会作为微任务排队。可以使用 Promise.all 来并行处理多个异步任务,将多个 await 合并为一个:
// 不好的写法,短时间内产生大量微任务
for (let i = 0; i < 1000; i++) {
    const result = await someAsyncFunction(i);
    // 处理 result
}
// 好的写法,使用 Promise.all
const promises = Array.from({ length: 1000 }, (_, i) => someAsyncFunction(i));
const results = await Promise.all(promises);
// 处理 results
  1. 合理安排微任务顺序:将对性能影响较小的微任务放在后面执行。例如,如果有一些更新 UI 的微任务和一些数据计算的微任务,优先执行数据计算微任务,避免 UI 更新的频繁阻塞。

实际项目中性能问题场景及优化方案

  1. 场景:在一个实时数据更新的 Web 应用中,需要频繁从服务器获取数据并更新 UI。假设使用 async/await 来处理每个数据获取请求:
async function updateData() {
    while (true) {
        const data = await fetchData();
        updateUI(data);
        await new Promise(resolve => setTimeout(resolve, 1000));
    }
}

这里每次获取数据和更新 UI 操作都会产生微任务。如果 fetchData 操作频繁且响应较快,会导致微任务队列堆积,可能造成 UI 卡顿。 2. 优化方案: - 节流与防抖:可以对数据获取操作进行节流或防抖处理。例如,使用防抖函数,确保在一定时间内只有一次数据获取请求,避免短时间内过多的微任务产生:

function debounce(func, delay) {
    let timer;
    return function() {
        const context = this;
        const args = arguments;
        clearTimeout(timer);
        timer = setTimeout(() => {
            func.apply(context, args);
        }, delay);
    };
}
const debouncedFetchData = debounce(fetchData, 500);
async function updateData() {
    while (true) {
        const data = await debouncedFetchData();
        updateUI(data);
        await new Promise(resolve => setTimeout(resolve, 1000));
    }
}
- **合并请求**:如果可能,将多个数据获取请求合并为一个。例如,服务器支持批量获取数据,就可以一次性获取多个数据块,减少请求次数,从而减少微任务数量。