MST

星途 面试题库

面试题:JavaScript异步可迭代对象与并发控制

创建一个异步可迭代对象,该对象的迭代器每次返回一个Promise,代表一个耗时的异步任务(例如通过`setTimeout`模拟)。要求实现一个机制,能够限制同时执行的异步任务数量为3个,当有任务完成时,自动启动新的任务,直到所有任务执行完毕。请完整实现该功能,并解释你的设计思路。
26.6万 热度难度
编程语言JavaScript

知识考点

AI 面试

面试题答案

一键面试
class AsyncIterableLimit {
    constructor(data, limit) {
        this.data = data;
        this.limit = limit;
        this.index = 0;
        this.pendingCount = 0;
        this.queue = [];
    }

    async *[Symbol.asyncIterator]() {
        while (this.index < this.data.length || this.pendingCount > 0) {
            while (this.pendingCount < this.limit && this.index < this.data.length) {
                const task = this.data[this.index++];
                this.pendingCount++;
                const result = await new Promise((resolve, reject) => {
                    this.queue.push({ task, resolve, reject });
                    this.runNext();
                });
                yield result;
            }
            await new Promise(resolve => setTimeout(resolve, 100));
        }
    }

    runNext() {
        if (this.queue.length === 0) return;
        const { task, resolve, reject } = this.queue.shift();
        setTimeout(() => {
            task()
              .then(resolve)
              .catch(reject)
              .finally(() => {
                    this.pendingCount--;
                    this.runNext();
                });
        }, 1000);
    }
}

// 使用示例
const data = [
    () => Promise.resolve(1),
    () => Promise.resolve(2),
    () => Promise.resolve(3),
    () => Promise.resolve(4),
    () => Promise.resolve(5),
    () => Promise.resolve(6)
];

const asyncIterable = new AsyncIterableLimit(data, 3);

(async () => {
    for await (const result of asyncIterable) {
        console.log(result);
    }
})();

设计思路

  1. 类的初始化
    • AsyncIterableLimit类接受一个包含异步任务的数组data和最大并发数limit作为参数。初始化一些计数器和队列。
  2. 异步迭代器实现
    • 通过实现Symbol.asyncIterator方法来使对象成为异步可迭代对象。
    • 使用while循环,只要还有未处理的数据或者有正在执行的任务,就持续迭代。
    • 在内部while循环中,只要当前正在执行的任务数小于限制并且还有未处理的数据,就取出新的任务并通过Promise包装,等待任务完成后yield结果。
  3. 任务执行控制
    • runNext方法负责从队列中取出任务并执行,执行完成后更新正在执行的任务数,并递归调用自身以执行下一个任务。
    • 每个任务通过setTimeout模拟异步操作。
  4. 使用示例
    • 创建AsyncIterableLimit实例,传入异步任务数组和限制数。
    • 使用for await...of循环迭代异步可迭代对象,依次处理每个任务的结果。