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


相关推荐
我是Superman丶38 分钟前
【技巧】前端VUE用中文方法名调用没效果的问题
前端·javascript·vue.js
斯~内克39 分钟前
Vue 3 中 watch 的使用与深入理解
前端·javascript·vue.js
lqj_本人3 小时前
鸿蒙OS&UniApp制作多选框与单选框组件#三方框架 #Uniapp
前端·javascript·uni-app
SHIPKING3936 小时前
【HTML】个人博客页面
javascript·css·html
junjun.chen06067 小时前
【在qiankun模式下el-dropdown点击,浏览器报Failed to execute ‘getComputedStyle‘ on ‘Window‘: parameter 1 is not o
前端·javascript·前端框架
課代表7 小时前
AcroForm JavaScript Promise 对象应用示例: 异步加载PDF文件
开发语言·javascript·pdf·promise·对象
zm8 小时前
服务器连接多客户端
java·javascript·算法
小屁孩大帅-杨一凡9 小时前
一个简单点的js的h5页面实现地铁快跑的小游戏
开发语言·前端·javascript·css·html
purpleseashell_Lili10 小时前
配置别名路径 @
javascript·react
这个一个非常哈10 小时前
VUE篇之自定义组件使用v-model
前端·javascript·vue.js