手动实现 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、任务调度


相关推荐
程序员大阳7 小时前
副队长JavaScript教程(49)-主动抛出异常与错误类型
javascript·异常处理·error·throw·错误类型
hhhjhl7 小时前
Protobuf.js 跨端三方库仓颉适配全流程
javascript·网络·udp
Flynt10 小时前
我把热榜那篇"禁用 axios"逐条实测了一遍:结论大体对,坑在它没讲的地方
前端·javascript·axios
slongzhang_11 小时前
linux 递归删除源码文件|递归删除目录下 *.js,但排除 *.min.js
linux·开发语言·javascript
liyi_hz200814 小时前
O2OA(翱途) V10.1 即将发布|功能再升级,安全再加固,协同更高效!
java·javascript·windows·开源软件
志尊宝15 小时前
AI原生前端开发(029):AI辅助的文档自动生成
javascript·人工智能·ai-native
一个游离的指针15 小时前
ES6新特性:let、const、块级作用域
开发语言·javascript·es6
Hilaku16 小时前
删了一个没用的 console.log,生产环境直接崩了!
前端·javascript·程序员
真上帝的左手16 小时前
2. 编程语言-JavaScript
开发语言·javascript
2601_9498477516 小时前
Form-data 跨端三方库仓颉适配全流程
前端·javascript·flutter