前端并发——基于 Promise.race 实现异步并发控制

在论坛闲逛时,无意中看到这篇帖子 大厂面试:别拯救了,拯救不了一点,第一感觉就是,现在前端实习面试这么卷了吗 😂,都考上异步并发控制了。正好自己比较感兴趣,而且上次思考这个问题已经很久了有点生疏,于是马上动手尝试解决!

题目

转载自该贴的原题是这样:

场景:把fn当作一个请求,现在creator中的2表示限制一次性最多只能发送2次请求,超过了2次就需要等上一个执行完毕才能继续。

试问:现连续执行了5次setup请求,如何在creator中限制它的请求数

给出的空壳代码为:

js 复制代码
function creator(count) {
    // fn: () => Promise
    function setup(fn) {

    }
    return setup
}
const setup = creator(2)

setup(fn)
setup(fn)
setup(fn)
setup(fn)
setup(fn)

function fn() {
    return new Promise((resolve) => {
        setTimeout(() => {
            console.log(1)
            resolve(1)
        }, 2000)
    })
}

思路

并发思路大概是这样:

  1. 利用 Promise.race 可以处理执行最快的 Promise 对象这个特性,可以初始化一个大小为并发数的执行池用 race 执行。
  2. 有执行池就需要替换掉已经执行完成的任务。只要在执行后加一个 then 方法返回当前任务的数组下标就可以进行替换处理。

题目还是比较有意思的,比起常见的并发控制器的封装传参为并发控制数和异步方法数组,这里支持通过多次调用去动态增加需要执行的异步函数。

我们都知道宏任务是在下一次事件循环才会进行执行调用,那么这里我们可以将整个并发放在一个宏任务 里边,就支持在同步代码里(应该也可以在微任务里)多次调用setup()去进行并发处理。

这里的思路描述比较简单,具体可以看实现代码的注释去理解~~

实现

为了更好的验证并发控制是否成功,这里我将题目里的打印改成打印 Date() ,具体完整代码如下

js 复制代码
function creator(count) {
    const arr = [];
    let flag = true;

    function setup(fn) {
        arr.push(fn);
        // 包一层异步,让同步调用全部进来
        flag && setTimeout(() => {
            // 并发执行容器,初始化长度为 count
            const promises = arr.splice(0, count).map((item, index) => {
                // 返回下标,用户执行容器的替换
                return item().then(() => index);
            })

            // 真正执行过程开始,此时 arr 中为第一轮排不上的异步任务
            arr.reduce((pres, curFn) => {
                return pres.then(() => {
                    return Promise.race(promises); // 返回先完成的下标
                }).then(fastestIndex => {
                    // 要继续将这个下标返回,以便下一次遍历
                    promises[fastestIndex] = curFn().then(() => fastestIndex);
                })
            }, Promise.resolve());
        }, 0) && (flag = false);// flag 控制异步只调用一次
    }
    return setup
}
const setup = creator(2)

setup(fn)
setup(fn)
setup(fn)
setup(fn)
setup(fn)

function fn() {
    return new Promise((resolve) => {
        setTimeout(() => {
            console.log(Date())
            resolve(1)
        }, 2000)
    })
}

执行结果:

从结果来看,也算是把这个题目给解决了!当然,异步并发控制有非常多的场景,有很多不同的、性能更好的解决方法,本文的方法只是作为一个参考。

如果大家有更好的思路或者对并发控制性能上有什么想法的话欢迎评论或者私信交流!👏

这里是 Storm⚡️,一个前端开发学习者

相关推荐
TheITSea19 分钟前
JavaScript Web APIs
开发语言·前端·javascript
开开心心就好40 分钟前
以图搜图找重复图片,本地工具离线就能用
javascript·智能手机·ffmpeg·c#·ocr·word·音视频
企业数字化笔记1 小时前
视频局部合成怎么做得自然?掩膜修复、边缘羽化、时序稳定与背景替换
java·javascript·音视频
liangshanbo12153 小时前
JavaScript 面试题:Object.keys() 的返回顺序是无序的吗?
开发语言·javascript·ecmascript
默_笙16 小时前
🚦 请三个 AI 考官给 RAG 打分:量化评估实战(下)
前端·javascript
阿坨16 小时前
灵感闪存卡:一款 Tampermonkey用户脚本
javascript·tampermonkey
朝朝辞暮i18 小时前
C++ 第 25 课:getter / setter + this
java·javascript·c++
蜗牛互联网18 小时前
Python接入Gemini 3.8 Flash实现票据视觉抽取与规则校验
java·javascript·网络·人工智能·python
xieter19 小时前
【技术精选】深入浅出现代前端渲染与性能优化实践指南 (2026-10-04)
前端·javascript
xieter19 小时前
【硬核实战】TypeScript Compiler API: Preserving Child Node Narrowing in Reusable Type Guards 🔧 (2026-10-03)
前端·javascript