MST

星途 面试题库

面试题:JavaScript中Promise与异步操作的优化及并发控制

现在有一个需求,需要同时发起多个异步请求(假设为10个,每个请求返回Promise),但为了避免对服务器造成过大压力,同一时间只能有3个请求处于活跃状态。当有一个请求完成后,立刻从剩余请求中选取一个继续执行,直到所有请求都完成。请使用Promise实现这个并发控制的功能,并说明实现思路,同时考虑如何处理请求过程中的错误。
26.8万 热度难度
编程语言JavaScript

知识考点

AI 面试

面试题答案

一键面试

实现思路

  1. 我们可以使用一个计数器来记录当前正在执行的请求数量,初始值为0。
  2. 维护一个请求队列,将所有10个请求放入队列中。
  3. 每次从队列中取出一个请求执行,同时计数器加1。
  4. 当一个请求完成(无论成功还是失败),计数器减1,然后从队列中取出下一个请求执行,直到队列为空。
  5. 为了处理错误,我们可以在每个请求的Promise中使用.catch方法捕获错误,并在最终的Promise中统一处理。

代码实现

function asyncTask() {
    return new Promise((resolve, reject) => {
        setTimeout(() => {
            // 模拟异步操作,这里可以替换为实际的异步请求
            Math.random() > 0.5? resolve('success') : reject('error');
        }, 1000);
    });
}

function concurrentControl(tasks, maxConcurrent) {
    return new Promise((resolve, reject) => {
        let completedCount = 0;
        let error = null;
        const results = [];
        const queue = tasks.slice();
        const runningCount = 0;

        function next() {
            while (runningCount < maxConcurrent && queue.length > 0) {
                const task = queue.shift();
                task()
                  .then(result => {
                        results.push(result);
                        completedCount++;
                        if (completedCount === tasks.length) {
                            resolve(results);
                        }
                        next();
                    })
                  .catch(err => {
                        if (!error) {
                            error = err;
                            reject(error);
                        }
                        next();
                    });
            }
        }

        next();
    });
}

// 示例使用
const tasks = Array.from({ length: 10 }, () => asyncTask);
concurrentControl(tasks, 3)
  .then(results => {
        console.log('All tasks completed successfully:', results);
    })
  .catch(error => {
        console.log('An error occurred:', error);
    });

错误处理

  1. 在每个请求的Promise中使用.catch捕获错误。
  2. 如果有错误发生,首先检查error变量是否已经有值。如果没有,则将当前错误赋值给error,并调用reject拒绝最终的Promise。如果error已经有值,说明之前已经发生过错误,此时只需要调用next继续处理队列中的下一个请求即可,因为最终的Promise已经被拒绝,后续错误不需要重复处理。这样可以确保在第一个错误发生时,整个并发操作能够及时失败并返回错误信息,同时继续处理队列中的其他请求,保证队列中的所有任务都能得到执行机会。