Promise控制同时请求的数量

184 阅读2分钟

控制同时进行的请求数量代码实现

/**
 * 并发控制执行器 - 控制同时执行的任务数量
 * @param {Function[]} tasks - 任务函数数组,每个函数返回Promise
 * @param {number} maxConcurrent - 最大并发数
 * @returns {Promise<Array>} 返回所有任务的结果数组
 */
async function runWithConcurrency(tasks, maxConcurrent) {
    if (!Array.isArray(tasks) || tasks.length === 0) {
        return [];
    }
    
    if (maxConcurrent <= 0) {
        throw new Error('maxConcurrent 必须大于 0');
    }
    
    const results = []; // 存放所有任务的最终结果
    const activePromises = new Set(); // 当前正在执行的任务Promise集合
    
    // 执行单个任务的包装函数
    const executeTask = async (task, index) => {
        try {
            const result = await task();
            return { index, status: 'fulfilled', value: result };
        } catch (error) {
            return { index, status: 'rejected', reason: error };
        }
    };
    
    // 等待任意一个活跃任务完成
    const waitForAnyTask = async () => {
        if (activePromises.size === 0) return;
        await Promise.race(activePromises);
    };
    
    // 清理已完成的任务
    const cleanupCompletedTasks = () => {
        for (const promise of activePromises) {
            if (promise.status === 'fulfilled' || promise.status === 'rejected') {
                activePromises.delete(promise);
            }
        }
    };
    
    for (let i = 0; i < tasks.length; i++) {
        const task = tasks[i];
        
        // 如果达到最大并发数,等待任意一个任务完成
        while (activePromises.size >= maxConcurrent) {
            await waitForAnyTask();
            cleanupCompletedTasks();
        }
        
        // 创建并执行任务
        const taskPromise = executeTask(task, i);
        activePromises.add(taskPromise);
        results.push(taskPromise);
        
        // 任务完成后自动从活跃集合中移除
        taskPromise.finally(() => {
            activePromises.delete(taskPromise);
        });
    }
    
    // 等待所有任务完成
    return Promise.allSettled(results);
}

// 使用示例
const userIds = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10];

// 模拟异步获取用户信息的函数
const fetchUser = (id) => {
    return new Promise((resolve, reject) => {
        const delay = Math.random() * 2000 + 500; // 随机延迟 500-2500ms
        
        setTimeout(() => {
            // 模拟偶尔的失败情况
            if (Math.random() < 0.1) {
                reject(new Error(`获取用户 ${id} 失败`));
            } else {
                resolve({ id, name: `用户${id}`, delay: Math.round(delay) });
            }
        }, delay);
    });
};

// 将 fetchUser 调用包装成无参数的函数数组
const tasks = userIds.map(id => () => fetchUser(id));

// 测试函数
async function testConcurrency() {
    console.log('开始执行并发任务,最大并发数:3');
    console.log('任务总数:', tasks.length);
    
    const startTime = Date.now();
    
    try {
        const results = await runWithConcurrency(tasks, 3);
        
        const endTime = Date.now();
        const totalTime = endTime - startTime;
        
        console.log(`\n所有任务完成!总耗时: ${totalTime}ms`);
        console.log('结果统计:');
        
        const fulfilled = results.filter(r => r.status === 'fulfilled').length;
        const rejected = results.filter(r => r.status === 'rejected').length;
        
        console.log(`成功: ${fulfilled}, 失败: ${rejected}`);
        
        // 显示详细结果
        results.forEach((result, index) => {
            if (result.status === 'fulfilled') {
                console.log(`任务 ${index + 1}: ✅ ${JSON.stringify(result.value)}`);
            } else {
                console.log(`任务 ${index + 1}: ❌ ${result.reason.message}`);
            }
        });
        
    } catch (error) {
        console.error('执行过程中发生错误:', error);
    }
}

// 运行测试
testConcurrency();
  • 这个函数会确保最多只有maxConcurrent个请求同时在进行。
  • 当一个请求完成,池子里有空位了,才会开始下一个请求。