手动实现 promiseAll与带有并发控制的promiseAllWithConcurrencyLimit

手动实现 Promise.all 与并发控制的异步任务调度

引言

在 JavaScript 中,Promise.all 是一个非常强大的工具,可以并发执行多个异步任务并返回结果。然而,在某些场景下,我们可能需要对并发任务的数量进行限制,以避免资源过载。本文将带你手动实现 Promise.all 的功能,并进一步扩展实现一个带有并发控制的 promiseAllWithConcurrencyLimit 函数。


1. 手动实现 Promise.all

需求描述

  • 并发执行所有 Promise。
  • 按照任务顺序返回结果数组。
  • 如果有任务失败,立即返回第一个失败的结果。

代码实现

javascript 复制代码
var promiseAll = function(functions) {
    const res = new Promise((resolve, reject) => {
        const res = [];
        let num = 0;    // res.length 并不等价于已经完成的任务数量,因为如果直接res[2] = 2,那么前面的元素会被补充null,且length等于3
        for (let i = 0; i < functions.length; i++) {
            const func = functions[i];
            func()
                .then((resolvedValue) => {
                    res[i] = resolvedValue;
                    num++;
                })
                .catch((rejectedValue) => {
                    reject(rejectedValue)
                })
                .finally(() => {
                    if (num === functions.length) {
                        resolve(res)
                    }
                })
        }
    })
    return res;
};

示例

javascript 复制代码
const promise = promiseAll([() => new Promise(res => res(42))])
promise.then(console.log); // [42]

2. 带有并发控制的 promiseAllWithConcurrencyLimit

需求描述

  • 接受一个数组,数组内元素均为异步任务,返回 promises。
  • 对任务批量执行,每次有一个 limit 限制最大并发数量。
  • 执行完成后按照一开始的任务顺序返回结果数组。
  • 如果有失败返回第一个失败的结果。

模拟异步任务

javascript 复制代码
function fakeFetchFn(delay, res) {
    console.log(`${new Date().toLocaleTimeString()} fetching ${res}...`)
    return new Promise(resolve => {
        setTimeout(() => {
            resolve(res);
        }, delay)
    })
}

const tasks = [
    () => fakeFetchFn(1000, 1),
    () => fakeFetchFn(1000, 2),
    () => fakeFetchFn(1000, 3),
    () => fakeFetchFn(1000, 4),
    () => fakeFetchFn(1000, 5),
    () => fakeFetchFn(1000, 6),
    () => fakeFetchFn(1000, 7),
    () => fakeFetchFn(1000, 8),
    () => fakeFetchFn(1000, 9),
    () => fakeFetchFn(1000, 10),
]

代码实现

javascript 复制代码
function promiseAllWithConcurrencyLimit(tasks, limit) {
    const res = new Promise((resolve, reject) => {
        let numOfActive = 0;
        const res = [];
        let numOfCompletedTask = 0;
        let index = 0;
        // 递归调用
        function runTask() {
            while (index < tasks.length && numOfActive < limit) {
                const currentIndex = index; // 保存当前索引!!!
                index++; // 更新全局任务索引
                const task = tasks[currentIndex];
                numOfActive++;
                task()
                    .then((resolvedValue) => {
                        res[currentIndex] = resolvedValue;
                        numOfCompletedTask++;
                        numOfActive--;
                    })
                    .catch((rejectedValue) => {
                        reject(rejectedValue)
                    })
                    .finally(() => {
                        if (numOfCompletedTask === tasks.length) {
                            resolve(res)
                        } else {
                            runTask();
                        }
                    })
            }
        }
        runTask();
    })
    return res;
}

3. 示例与测试

无并发限制的测试

javascript 复制代码
// without limitation, 1-10 are printed at the same time
Promise.all(tasks.map(task => task())).then(console.log);
执行结果

有并发限制的测试

javascript 复制代码
// with limitation
promiseAllWithConcurrencyLimit(tasks, 2).then(console.log)
执行结果

4. 总结

通过本文,我们实现了以下功能:

  1. 手动实现 Promise.all:能够并发执行多个异步任务,并按照顺序返回结果。
  2. 并发控制的异步任务调度 :通过 promiseAllWithConcurrencyLimit,可以限制并发任务数量,优化资源使用。

这些技巧在实际开发中非常有用,尤其是在需要优化资源使用的场景中。希望本文能帮助你更好地理解 Promise 和并发控制的实现原理!


关键词

Promise.all、并发控制、异步任务、JavaScript、Promise、任务调度


相关推荐
问道飞鱼12 分钟前
【技术方案设计】H5埋点方案设计以及实现(入门版)
开发语言·javascript·h5·事件·埋点
撸码到无法自拔34 分钟前
❤React-组件的新旧生命周期
前端·javascript·react.js·前端框架·ecmascript
betterangela37 分钟前
react基础语法视图层&类组件
前端·javascript·vue.js
冴羽1 小时前
SvelteKit 最新中文文档教程(2)—— 路由
前端·javascript·svelte
虾球xz2 小时前
游戏引擎学习第153天
javascript·学习·游戏引擎
2401_853275732 小时前
ajax组件是什么
前端·javascript·ajax
呆头呆脑~2 小时前
同盾v2 2025版 blackbox , wasm加解密,逆向协议算法生成,小盾安全
javascript·爬虫·python·算法·网络爬虫·wasm
西柚i3 小时前
开发工具链的智能化重构
前端·javascript
努力的白熊嗨3 小时前
前端如何发布npm包
前端·javascript