前端并发控制
本文讲解Promise,callback,RxJS多种方式实现并发限制
1.Promise
目前来说,Promise是最通用的方案,一般我们最先想到Promise.all,当然最好是使用新出的Promise.allsettled。
下面简单介绍下二者的区别,假如存在某个请求失败时,all会整体失败,而allsettled只会让单个请求失败,对于大部分情况来说,allsettled的是更好的选择,因为allsettled更为灵活,一般来说面对这种情况,总共有三种处理方式,如下所示,all只能支持第一种,而allsettled三种都支持:
-
整体失败
-
最终结果,过滤失败的选项
-
将单个失败的保留,并渲染到UI中
方法1 全部并发
直接使用Promise.all是最简单的,代码如下,然后all并没有并发控制能力,一瞬间会将全部请求发出,从而造成前面提到的浏览器卡顿问题。
这里get函数我们使用setTimeout+随机时间来模拟请求,其返回promise实例。
function gets(ids, max) {return Promise.all(ids.map(id => get(id)))
}function get(id) {return new Promise((resolve) => {setTimeout(() => { resolve({ id }) }, Math.ceil(Math.random() * 5))});
}
方法2 分批并发
你可能会想到一种分批发送的办法,将请求按max数量分成N个组,每组并行发送,这需要结合递归和Promise.all,示例代码如下:
function gets(ids, max) {let index = 0;const result = [];function nextBatch() {const batch = ids.slice(index, index + max);index += max;return Promise.all(batch.map(get)).then((res) => {result.push(...res);if (index < ids.length) {return nextBatch();}return result;});}return nextBatch();
}
这种方法的优势在于实现相对简单,容易理解。但是它的缺点是,每一批请求中的最慢的请求会决定整个批次的完成时间,这可能会导致一些批次的其他请求早早完成后需要等待,从而降低整体的并发效率。
这种方法在业务中是不太能接受的,面试中的话,也只能勉强及格。
方法3 限制并发
一个更高效的思路是使用异步并发控制,而不是简单的批处理。这种方法可以在任何时刻都保持最大数量的并发请求,而不需要等待整个批次完成。这需要我们维护一个请求池,在每个请求完成时,将下一个请求添加到请求池中,示例代码如下:
gets函数返回一个promise,在请求全部完成后,promise变为fulfilled状态;内部采用递归,每个请求成功和失败后,发送下一个请求;在最下面先发送max个请求到请求池中。
function gets(ids, max) {return new Promise((resolve) => {const res = [];let loadcount = 0;let curIndex = 0;function load(id, index) {return get(id).then((data) => {loadcount++;if (loadcount === ids.length) {res[index] = data;resolve(res);} else {curIndex++;load(ids[curIndex]);}},(err) => {res[index] = err;loadcount++;curIndex++;load(ids[curIndex]);});}for (let i = 0; i < max && i < ids.length; i++) {curIndex = i;load(ids[i], i);}});
}
2.callback
在Promise之前,js中的异步都是基于回调函数的,比如 jQuery 的 ajax,Node.js 中的 http 模块等。
茴字有多种写法,下面我们挑战一下使用callback来解决这个问题。下面我们先把get函数改造一下,基于回调函数的get如下所示:
function get(id, success, error) {setTimeout(() => success({ id }), Math.ceil(Math.random() * 5))
}
gets函数的接口也要改成回调函数,如下所示:
function gets(ids, max, success, error) {}
回调函数也是基于上面的思路,把上面的代码稍加改动即可,将其中的Promise换成callback,示例如下:
还记得前面让你想其他思路吗,还有一种结合递归和异步函数的方法,在Promise下会比这种方法更简单,但其实还是这个思路更好,Promise和callback都可以使用。
function gets(ids, max, success, error) {const res = [];let loadcount = 0;let curIndex = 0;function load(id, index) {return get(id,(data) => {loadcount++;if (loadcount === ids.length) {res[index] = data;success(res);} else {curIndex++;load(ids[curIndex]);}},(err) => {res[index] = err;loadcount++;curIndex++;load(ids[curIndex]);});}for (let i = 0; i < max && i < ids.length; i++) {curIndex = i;load(ids[i], i);}
}
3.RxJS
看看RxJS,绝大部分人都不太了解RxJS,RxJS号称异步编程的lodash,对于这个问题,其代码实现会非常简单。
RxJS 是一个用于处理异步数据流的 JavaScript 库,它通过「可观察对象」(Observable)来代表随时间推移发出值的数据流。你可以使用一系列操作符(如
map、filter、merge等)来处理这些数据流,并通过「订阅」(subscribe)来观察并执行相关操作。RxJS 使得处理复杂的异步逻辑变得简单而优雅,特别适合于实现并发控制等场景。
上面是RxJS的简介,相信看完了还是不理解,RxJS其实是比较难学的,建议大家阅读其他扩展资料。
下面先用RxJS改造我们的get函数,改造完如下所示,这需要用到Observable和observer,这些都是RxJS的概念,即便不知道其含义,看代码和Promise是比较相似的。
import { Observable } from 'RxJS';function get(id) {return new Observable((observer) => {setTimeout(() => {observer.next({ id });observer.complete();}, Math.ceil(Math.random() * 5));});
}
下面我们参考Promise中的思路,依次看看在RxJS中如何实现。
方法1 全部并发
在RxJS中和Promise.all类似的功能是forkJoin,这种方法最简单,代码如下所示,和Promise.all类似,这并不满足我们的需求。
import { forkJoin } from 'RxJS';function gets(ids) {const observables = ids.map(get);return forkJoin(observables);
}
方法2 分批并发
下面来看下如何实现分批并发,在Promise中我们使用递归+Promise.all来实现的。
在RxJS中,我们使用concatMap操作符来确保这些组是依次处理的,而不是同时处理。在处理每个组时,我们使用forkJoin来并行处理组内的所有请求。最后,我们使用reduce操作符来将所有组的结果合并成一个一维数组。
如果不理解RxJS,我们单纯看代码,可以看到RxJS代码的表现性更强,通过语义化的操作符串联,就完成了Promise中很多命令式的代码。
import { from, forkJoin } from 'RxJS';
import { concatMap, reduce } from 'RxJS/operators';function gets(ids, max) {// 将ids按max分组const groups = [];for (let i = 0; i < ids.length; i += max) {groups.push(ids.slice(i, i + max));}// 使用concatMap控制组之间的串行执行,并在每一组内使用forkJoin实现并行请求// 使用reduce来收集和合并所有组的结果return from(groups).pipe(concatMap((group) => forkJoin(group.map(get))),reduce((acc, results) => acc.concat(results), []));
}
方法3 限制并发
最后我们来看看RxJS如何实现限制并发,在这个实现中,我们使用mergeMap来控制并发,并使用一个Map对象来存储每个请求的结果,其中键是ID,值是请求结果。这样,我们可以在所有请求完成后,按照原始ID数组的顺序从Map中提取结果。
示例代码如下,控制并发是RxJS支持的功能,实现就是一个参数,非常简单
function gets(ids, max) {return from(ids).pipe(mergeMap((id) => get(id).pipe(map(result => ({ id, result }))), max),reduce((acc, { id, result }) => acc.set(id, result), new Map()),map(resMap => ids.map(id => resMap.get(id))));
}
总结
我们探讨了使用Promise,callback和RxJS的方式实现并发限制,每种方式中又介绍了三种代码思路,包括全部并发、分批并发以及限制并发。每种方法都有其适用场景和优缺点:
-
「全部并发」适用于需要将请求分批次处理的场景,简单易懂,但可能不是最高效的方法。
-
「分批并发」在保持一定并发度的同时,避免同时发出过多的请求,适用于需要控制资源消耗的场景。
-
「限制并发」则结合了并发的高效性和结果顺序的一致性,适用于对结果顺序有要求的并发请求处理。
通过选择合适的方法,我们可以在保证性能的同时,满足不同场景下对并发控制的需求。
再次给大家安利RxJS,RxJS作为一个强大的响应式编程库,为我们提供了灵活而强大的工具来处理这些复杂的异步逻辑。
文章的最后,我想引申下请求层的概念,在实际项目中,请求层的设计和实现对整个应用的性能和稳定性至关重要。一个健壮的请求层不仅能够处理基本的数据请求和响应,还能够应对各种复杂的网络环境和业务需求。以下是请求层可以处理的一些常见问题:
-
「失败和错误处理」:优雅地处理请求失败和服务器返回的错误,提升用户体验。
-
「失败重试」:在请求失败时自动重试,增加请求的成功率。
-
「接口降级」:在服务不可用时,提供备选方案,保证应用的基本功能。
-
「模拟接口」:在后端服务尚未开发完成时,模拟接口响应,加速前端开发。
-
「模拟列表接口」:模拟分页、排序等列表操作,方便前端调试和测试。
-
「接口聚合和竞态」:合并多个接口请求,减少网络开销;处理接口请求的竞态问题,确保数据的一致性。
-
「逻辑聚合」:将多个资源的创建和更新等操作聚合成一个请求,简化前端逻辑。
-
「控制并发数量」:限制同时进行的请求数量,避免过度消耗资源。
-
「前端分页」:在前端进行数据分页,减轻后端压力。
-
「超时设置」:为每个请求设置超时时间,防止长时间等待。
通过在请求层中实现这些功能,我们可以使得前端应用更加稳定和可靠,同时也提升了用户的体验。因此,「加强请求层的建设」是每个前端项目都应该重视的一个方面。
相关文章:
前端并发控制
本文讲解Promise,callback,RxJS多种方式实现并发限制 1.Promise 目前来说,Promise是最通用的方案,一般我们最先想到Promise.all,当然最好是使用新出的Promise.allsettled。 下面简单介绍下二者的区别,假…...
基于YOLOv8深度学习的橙子病害智能诊断与防治系统【python源码+Pyqt5界面+数据集+训练代码】深度学习实战、目标分类
《博主简介》 小伙伴们好,我是阿旭。专注于人工智能、AIGC、python、计算机视觉相关分享研究。 ✌更多学习资源,可关注公-仲-hao:【阿旭算法与机器学习】,共同学习交流~ 👍感谢小伙伴们点赞、关注! 《------往期经典推…...
Java中的多线程详解(超级简单理解)(上篇)
使用工具 IntelliJ IDEA Community Edition 2023.1.4 使用语言 Java8 代码能力快速提升小方法,看完代码自己敲一遍,十分有用 目录 1.多线程概述 1.1 进程与线程 1.2 多线程的运行机制 1.3 多线程的优势 2.多线程编程 2.1 Thread类介绍 2.2 …...
Elastic-Job 分布式任务调度
一、使用场景 (1)分布式项目中 定时任务。如果只部署一台机器,可用性无法保证,如果定时任务机器宕机,无法故障转移,如果部署多台机器时,同一个任务会执行多次,任务重复执行也会出问…...
YZ系列工具之YZ09: VBA_Excel之读心术
我给VBA下的定义:VBA是个人小型自动化处理的有效工具。利用好了,可以大大提高自己的工作效率,而且可以提高数据的准确度。我的教程一共九套一部VBA手册,教程分为初级、中级、高级三大部分。是对VBA的系统讲解,从简单的…...
Python下载音乐
今天我就来分享一下我的方法:Python爬虫 在CS dn社区中我浏览了许多关于爬虫代码,可都有各自的缺陷,有的需要ID比较麻烦,这里我编写了一个程序,他只需要输入歌曲名字即可进行搜索爬取并下载 话不多说,下面的程序复制…...
PCL ICP配准高阶用法——统计每次迭代的配准误差并可视化
目录 一、概述二、代码实现三、可视化代码四、结果展示本文由CSDN点云侠原创,原文链接。如果你不是在点云侠的博客中看到该文章,那么此处便是不要脸的爬虫。 一、概述 在进行论文写作时,需要做对比实验,来分析改进算法的性能,期间用到了迭代误差分布统计的比较分析,为直…...
电脑卸载软件怎么清理干净?电脑清理的5种方法
随着我们在电脑上安装和卸载各种软件,很多时候我们会发现,即使软件被卸载,其残留的文件和注册表项仍然存在于电脑中,这不仅占用了宝贵的磁盘空间,还可能影响电脑的性能。那么,如何确保在卸载软件时能够彻底…...
LLM流式方案解决方案和客户端解决方案
背景 接上一篇《LLM大模型统一封装接口解决方案》架构确定后,流式方案非常规请求,需要特殊处理。 本解决方案就是针对上一篇中所需要的流式(打字机效果进行编码) 什么是SSE SSE(Server-Sent Events,服务器发…...
ROS2 高效学习系列
ROS2 高效学习系列 1 说明2 正文 1 说明 2023 年,我们总结输出了 ROS高效入门系列 和 ROS高效进阶系列,系统学习了 ros1 基础知识和 ros 的机器人算法。由于 ros2 的普及,我们将系统学习 ros2 ,包括 ros2 基础知识和相关高级组件…...
SpringBoot + MyBatisPlus分页查询
文章目录 1.思路分析2.分页查询后端实现1.com/sun/furn/config/MybatisConfig.java 注入MyBatisPlus分页拦截器2.com/sun/furn/controller/FurnController.java 添加方法3.postman测试 3.分页查询前端实现1.src/views/HomeView.vue 引入分页导航条组件2.src/views/HomeView.vue…...
记使用sjson的一次小事故
1. 前言 之前在设计一个兼容函数的时候,使用了sjson动态设入参数,从而实现一些参数的兼容。大致的逻辑如下所示: // 有一堆不规则的json数据 {"a":"aaa","b":"bbb","any_key1":{"k…...
如何在iOS系统抓取log
前言:因为作者目前工作领域和苹果智能家居有关,然后发现一些bug其实是apple sdk原生code的问题,所以需要给apple提radar单,就需要抓ios端Log充当证据给apple看,其实ios抓log非常简单,大家感兴趣可以学习下哦…...
【嵌入式——QT】Charts常见的图表的绘制
【嵌入式——QT】Charts常见的图表的绘制 柱状图QBarSetQBarSeriesQBarCategoryAxis图示 饼图堆叠柱状图百分比柱状图散点图和光滑曲线图代码示例 柱状图 QBarSet 用于创建柱状图的数据集。 主要函数 setLabel():设置数据集标签 ;setLabelBrush()&am…...
pandas读写excel,csv
1.读excel 1.to_dict() 函数基本语法 DataFrame.to_dict (self, orientdict , into ) --- 官方文档 函数种只需要填写一个参数:orient 即可 ,但对于写入orient的不同,字典的构造方式也不同,官网一共给出了6种,…...
清华大学突破性研究:GVGEN技术,7秒内从文字到3D高保真生成
引言:3D模型生成的挑战与机遇 随着计算机图形学的发展,3D模型的生成在各个行业中变得越来越重要,包括视频游戏设计、电影制作以及AR/VR技术等。在3D建模的不同方面中,从文本描述生成3D模型成为一个特别有趣的研究领域,…...
软件测试要学习的基础知识——黑盒测试
概述 黑盒测试也叫功能测试,通过测试来检测每个功能是否都能正常使用。在测试中,把程序看作是一个不能打开的黑盒子,在完全不考虑程序内部结构和内部特性的情况下,对程序接口进行测试,只检查程序功能是否按照需求规格…...
如何用Airtest脚本连接无线Android设备?
之前我们已经详细介绍过如何用AirtestIDE无线连接Android设备,它的关键点在于,需要先 adb connect 一次,才能点击 connect 按钮无线连接上该设备: 但是有很多同学,在使用纯Airtest脚本的形式连接无线设备时,…...
c语言函数大全(C开头)
c语言函数大全(C开头) There is no nutrition in the blog content. After reading it, you will not only suffer from malnutrition, but also impotence. The blog content is all parallel goods. Those who are worried about being cheated should leave quickly. 函数名…...
初始Redis关联和非关联
基础篇Redis 3.初始Redis 3.1.2.关联和非关联 传统数据库的表与表之间往往存在关联,例如外键: 而非关系型数据库不存在关联关系,要维护关系要么靠代码中的业务逻辑,要么靠数据之间的耦合: {id: 1,name: "张三…...
5步打造清爽右键菜单:ContextMenuManager开源工具完全指南
5步打造清爽右键菜单:ContextMenuManager开源工具完全指南 【免费下载链接】ContextMenuManager 🖱️ 纯粹的Windows右键菜单管理程序 项目地址: https://gitcode.com/gh_mirrors/co/ContextMenuManager 当你在Windows系统中右键点击文件时&#…...
3步轻松搞定大麦网抢票:告别手动刷票的Python自动化脚本
3步轻松搞定大麦网抢票:告别手动刷票的Python自动化脚本 【免费下载链接】DamaiHelper 大麦网演唱会演出抢票脚本。 项目地址: https://gitcode.com/gh_mirrors/dama/DamaiHelper 还在为抢不到演唱会门票而烦恼吗?🤔 热门演出门票秒光…...
告别编译报错!Termux安装Pandas最稳方案实测(附Matplotlib、Numpy、Scipy一键配置清单)
Termux科学计算环境搭建:零报错安装Pandas与数据三件套实战指南 在移动端进行Python数据分析曾是天方夜谭,直到Termux的出现打破了这一限制。但许多用户在安装Pandas、Numpy、Scipy和Matplotlib这组"数据科学四件套"时,总会遇到各种…...
Token 成本暴跌 280 倍,为什么用 AI 替代初级开发,依然算不拢账?
从董事会的 PPT 翻车,看 AI 降本神话背后的全成本真相上周我旁听了一场 C-suite 高管会议,亲眼看着一位副总裁被自己的 PPT 逼入绝境。会议的主题是 AI 项目的成本收益,他准备了一套无懈可击的逻辑:大模型 Token 价格 3 年暴跌 28…...
Qwen3.5-9B-AWQ-4bit镜像免配置教程:无需conda/pip,7860端口直连使用
Qwen3.5-9B-AWQ-4bit镜像免配置教程:无需conda/pip,7860端口直连使用 1. 开箱即用的视觉理解模型 今天要介绍的是一个能"看懂"图片的AI模型——Qwen3.5-9B-AWQ-4bit。这个模型最厉害的地方在于,它不仅能理解文字,还能…...
人工智能之数字生命 认知架构白皮书 第4章
《HY-Ego 认知架构白皮书》(续)4. 世界树(World Tree)——全局世界骨架 世界树是 HY-Ego 认知架构的全局事实骨架,负责对整个“世界”进行结构化建模、组织和维护。它与因果树并行独立运行,二者通过快照机制…...
Flutter鸿蒙化适配中遇到的问题
Flutter 环境搭建避坑指南Flutter 作为跨平台开发的热门框架,凭借一套代码多端运行的优势,深受开发者喜爱,但环境搭建与适配却是新手入门的第一道拦路虎。我在初次配置 Flutter 开发环境时,接连踩中环境变量、模拟器版本、第三方工…...
OpenClaw私人健身教练:Qwen2.5-VL-7B分析运动视频与生成计划
OpenClaw私人健身教练:Qwen2.5-VL-7B分析运动视频与生成计划 1. 为什么需要AI健身教练 去年夏天,我在健身房遇到一个尴尬场景:深蹲时被教练提醒"膝盖内扣"已经持续了三周却毫无察觉。这种滞后反馈让我开始思考——能否用AI实现实…...
Grove-I2C颜色传感器驱动开发与RGB色彩识别实践
1. Grove-I2C颜色传感器技术解析与嵌入式驱动开发实践 1.1 模块硬件架构与传感原理 Grove-I2C颜色传感器模块基于TAOS(现为ams OSRAM)TCS3414CS高精度数字颜色传感器芯片设计,其核心传感单元由16个微型光电二极管阵列构成,呈82物…...
成为数据科学家之路,第一部分:数学
原文:towardsdatascience.com/roadmap-to-becoming-a-data-scientist-part-1-maths-2dc9beb69b27 https://github.com/OpenDocCN/towardsdatascience-blog-zh-2024/raw/master/docs/img/492ae0fb35397ff6690bc9518f937530.png 简介 数据科学无疑是当今最迷人的领域…...
