MST

星途 面试题库

面试题:JavaScript 异步迭代器在复杂应用场景中的性能优化

在一个高并发的 Web 应用场景中,有大量的异步任务需要通过异步迭代器和 for - await - of 循环来处理。这些任务之间存在一定的依赖关系,并且部分任务可能会因为网络等原因失败并重试。请设计一个 JavaScript 方案,不仅要正确处理这些任务,还要考虑性能优化,比如如何避免过度等待、如何有效管理重试机制等。请详细阐述设计思路,并提供关键代码片段及解释。
35.4万 热度难度
编程语言JavaScript

知识考点

AI 面试

面试题答案

一键面试

设计思路

  1. 任务依赖管理:使用一个数据结构(如数组)来表示任务及其依赖关系。每个任务可以包含一个唯一标识、任务函数、依赖任务的标识列表。
  2. 异步迭代与执行:利用 for - await - of 循环结合异步迭代器来按顺序处理任务。确保在执行一个任务前,其所有依赖任务都已成功完成。
  3. 重试机制:为每个任务定义重试次数和重试间隔。当任务失败时,按照设定的重试次数和间隔进行重试。
  4. 性能优化
    • 避免过度等待:在等待依赖任务完成时,使用 Promise.race 来监控所有依赖任务的完成情况,而不是逐个等待。
    • 有效管理重试:随着重试次数增加,适当增加重试间隔,避免短时间内大量重试请求对系统造成压力。

关键代码片段及解释

// 定义任务结构
const tasks = [
    { id: 'task1', fn: async () => { /* 任务逻辑 */ }, dependencies: [] },
    { id: 'task2', fn: async () => { /* 任务逻辑 */ }, dependencies: ['task1'] },
    { id: 'task3', fn: async () => { /* 任务逻辑 */ }, dependencies: ['task2'] }
];

// 异步迭代器生成函数
function* taskIterator(tasks) {
    for (const task of tasks) {
        yield task;
    }
}

// 执行任务的主函数
async function executeTasks() {
    const taskIter = taskIterator(tasks);
    const completedTasks = new Map();

    for await (const task of taskIter) {
        const { id, fn, dependencies } = task;

        // 等待所有依赖任务完成
        const dependencyPromises = dependencies.map(depId => {
            if (!completedTasks.has(depId)) {
                throw new Error(`Dependency ${depId} not completed for task ${id}`);
            }
            return completedTasks.get(depId);
        });
        await Promise.race(dependencyPromises);

        let retryCount = 3; // 重试次数
        let retryInterval = 1000; // 初始重试间隔 1 秒
        while (retryCount > 0) {
            try {
                const result = await fn();
                completedTasks.set(id, Promise.resolve(result));
                break;
            } catch (error) {
                retryCount--;
                if (retryCount === 0) {
                    throw new Error(`Task ${id} failed after ${3 - retryCount} retries: ${error.message}`);
                }
                await new Promise(resolve => setTimeout(resolve, retryInterval));
                retryInterval *= 2; // 每次重试间隔翻倍
            }
        }
    }
}

executeTasks().catch(console.error);
  • 任务结构定义tasks 数组定义了每个任务的唯一标识 id、执行函数 fn 以及依赖任务的标识列表 dependencies
  • 异步迭代器生成函数taskIterator 函数返回一个生成器,用于按顺序迭代任务。
  • 执行任务主函数
    • taskIter 是异步迭代器实例。completedTasks 用于记录已完成任务的结果。
    • for - await - of 循环中,针对每个任务,先通过 Promise.race 等待所有依赖任务完成。
    • 任务执行失败时,按照设定的重试次数和逐渐翻倍的重试间隔进行重试。若最终仍失败,则抛出错误。