前端处理并发的最佳实践
什么是并发?
因为js是单线程的,所以前端的并发指的是在极短时间内发送多个数据请求,比如说循环中发送ajax。
举一个简单的例子:
下面一段代码是常规的mount阶段执行的请求:
useEffect(async () => {console.time();await TaskBizService.querySpyTaskSummary();await TaskBizService.querySpyTask();console.timeEnd();// time: 300ms
}, [])
更换成这样:
useEffect(() => {console.time();Promise.all([TaskBizService.querySpyTaskSummary(),TaskBizService.querySpyTask(),]).then((res) => {console.timeEnd();});// time: 120ms
}, [])
可以看出有很大的性能优化空间和区别,如果在页面渲染时,多个请求没有相互数据依赖性(依赖请求),直接采用并行请求会加快页面中数据展现的时间,这两个demo同时也涉及到事件循环的一些知识。
因此也可以在页面中找到一些可优化的请求,转换为并行,对于首屏渲染的优化是有很大帮助的。
Promise.all
可以采用Promise.all处理并发, 当所有promise全部成功时, 会走.then,并且可以拿到所有promise中传进resolve中的值。
看一下这段代码:
let p1 = new Promise((resolve, reject) => {setTimeout(() => {resolve('request1 end')}, 1000);
})let p2 = new Promise((resolve, reject) => {setTimeout(() => {resolve('request2 end')}, 3000);
})console.time(); // 开始计时
Promise.all([p1, p2])
.then(result => {console.timeEnd(); // default: 3.2sconsole.log(result); // (2) ['request1 end', 'request2 end']
})
如果Promise.all中有实例失败,整个并发会全部挂掉。
就像这段代码:
let p1 = new Promise((resolve, reject) => {setTimeout(() => {resolve('request1 end')}, 1000);
})let p2 = new Promise((resolve, reject) => {setTimeout(() => {reject('request2 fail')}, 3000);
})console.time(); // 开始计时
Promise.all([p1, p2])
.then(result => {// 不会走到这一步console.timeEnd(); // default: 3.2sconsole.log(result);
})
总结
-
Promise.all在处理并发时,如果有一个promise失败,就不会走.then, 但是如果只是需要在所有异步执行完成之后去执行其它副作用代码,可以把代码写在.finally中实现。 -
但是如果需要在有异步失败之后,获取到
promise实例通过resolve传过来的值,则不行。
Promise.allSettled
在Promise.all中实现不了的功能, 可以使用Promise.allSettled来实现, 在Promise.allSettled中,当其中有一个promise执行失败时,会继续走.then, 并且可以同时拿到传给resolve、reject的值,可以通过回调值来判断接口的请求结果来做二次处理。
代码改造起来也非常简单,如下:
let p1 = new Promise((resolve, reject) => {setTimeout(() => {resolve('request1 end')}, 1000);
})let p2 = new Promise((resolve, reject) => {setTimeout(() => {resolve('request2 end')}, 3000);
})console.time(); // 开始计时
Promise.allSettled([p1, p2])
.then(result => {console.timeEnd(); // default: 3.2sconsole.log(result); // (2) ['request1 end', 'request2 end']
})
当请求结果全部resolve的情况,Promise.allSettled和Promise.all没有太大区别,只是对于catch的情况下做了一次弥补。
同时Promise.allSettled的兼容性并没有Promise.all来得好。
总结
-
Promise.allSettled在处理并发时,不怕异步执行失败,继续会走.then,完美解决Promise.all在并发有失败情况下,拿不到值的情况 -
唯一的缺点,比起
Promise.all兼容性差一点,多了两个不支持的浏览器, 安卓端火狐浏览器(Firefox for Android)及 Samsung Internet 浏览器。
async/await
async/await能处理并发吗?答案是可以的,但是代码会看起来臃肿不易读一下,先看一下常规的async/await的异步处理方案吧。
代码如下:
let getData1 = () => {return new Promise((resolve, reject) => {setTimeout(() => {resolve('request1 end')}, 2000);})
};let getData2 = () => {return new Promise((resolve, reject) => {setTimeout(() => {resolve('request2 end')}, 3000);})
}
let syncFn = async () => {console.time(); // 开始计时const data1 = await getData1();const data2 = await getData2();console.timeEnd(); // default: 5.8sconsole.log(data1, data2); // request1 end, request2 end
}syncFn();
从耗时可以看到,async/await造成了异步阻塞,实际走的是串行(依赖)请求,也是普通项目中最常见的处理方式,接下来我们改造一下async/await并行请求的方案。
let getData1 = () => {return new Promise((resolve, reject) => {setTimeout(() => {resolve('request1 end')}, 2000);})
};let getData2 = () => {return new Promise((resolve, reject) => {setTimeout(() => {resolve('request2 end')}, 3000);})
}
let syncFn = async () => {console.time(); // 开始计时const p1 = getData1();const p2 = getData2();const data1 = await p1;const data2 = await p2;console.timeEnd(); // default: 3.8sconsole.log(data1, data2); // request1 end, request2 end
}syncFn();
总结
-
如果是在安装了
axios或者其它请求库,在这些库本身提供支持并发的api情况下,如axios.all、axios.spread,建议使用他们提供的这些api,因为作为一个百万级下载量的库,提供的这些api考虑到的边界条件、兼容性肯定是比其它原生方法好用的。 -
在没有这些库的情况下,推荐使用
Promise.allSettled,如果你喜欢用async、await也可以,结果没有区别,看个人喜好了。但是在我看来在处理并发的情况下async、await的写法感觉就不那么简洁了。
相关文章:
前端处理并发的最佳实践
什么是并发? 因为js是单线程的,所以前端的并发指的是在极短时间内发送多个数据请求,比如说循环中发送ajax。 举一个简单的例子: 下面一段代码是常规的mount阶段执行的请求: useEffect(async () > {console.time…...
【SOP 】配电网故障重构方法研究【IEEE33节点】(Matlab代码实现)
💥💥💞💞欢迎来到本博客❤️❤️💥💥 🏆博主优势:🌞🌞🌞博客内容尽量做到思维缜密,逻辑清晰,为了方便读者。 ⛳️座右铭&a…...
[MySQL索引]4.索引的底层原理(三)
索引的底层原理(三)哈希索引InnoDB自适应哈希索引哈希索引 memory存储引擎支持的是哈希索引,memory是支持内存的存储引擎。 哈希表中的元素没有任何顺序可言,只能进行等值比较,包括范围搜索、前缀搜索like、order by…...
2023金三银四应届生求职面试指南
一、应届生优势 划重点,一定要走校招;千万不要等毕业之后再想着找工作,在毕业前就要敲定落实;否则,就真的该焦虑了。要知道应届生的身份是一个很吃香的身份;只有应届生可以走校园招聘。 1、那校园招聘跟社会招聘有多大的差距?? 这么说吧&…...
【数据结构】解决顺序表题的基本方法
🚀write in front🚀 📜所属专栏:> 初阶数据结构 🛰️博客主页:睿睿的博客主页 🛰️代码仓库:🎉VS2022_C语言仓库 🎡您的点赞、关注、收藏、评论࿰…...
HDFS如何解决海量数据存储及解决方案详解
HDFS组件 HDFS组件的基准测试 说明 一般在搭建完集群之后,运维人员需要对集群进行压力测试,对于HDFS来讲,主要是读写测试写入测试 hadoop jar /export/server/hadoop-3.3.0/share/hadoop/mapreduce/hadoop-mapreduce-client-jobclient-3.…...
认识CSS值如何提高写前端代码的效率
🌟所属专栏:前端只因变凤凰之路🐔作者简介:rchjr——五带信管菜只因一枚😮前言:该系列将持续更新前端的相关学习笔记,欢迎和我一样的小白订阅,一起学习共同进步~👉文章简…...
MySQL知识点全面总结3:Mysql高级篇
三.MySQL知识点全面总结3:mysql高级篇 1.mysql语句的执行过程? 2.myesql事务详解? 3.mysql日志详解? 4.mysql的索引功能详解? 5.mysql的存储引擎详解? 6.mysql事务提交后数据与硬盘如何交互存储&…...
Spring注解开发之组件注册(二)
Spring注解开发之组件注册(一) 5.Import 给容器导入一个组件 给容器中注册组件 一、包扫描 组件标注注解(Controller/Service/Repository/Component) [自己写的类] 二、Bean [导入的第三包里面的组件] 三、Import [快速给容器中导入组件] (Import{…...
【web前端开发】CSS最常用的11种选择器
文章目录1.CSS介绍2.CSS的语言规则3.CSS的引入方式4.选择器标签选择器类选择器id选择器通配符选择器复合选择器后代选择器子代选择器并集选择器交集选择器伪类选择器hover伪类选择器active伪类选择器结构伪类选择器结语1.CSS介绍 CSS (Cascading Style Sheets,层叠样…...
微电影广告发展的痛点
微电影广告以不可阻挡之势进入大众生活中,企业利用微电影广告来进行企业形象塑造的例子比比皆是。于是乎,微电影广告在为企业塑造品牌形象方面上取得了可喜的效果,但也不可忽视,在这个发展过程中,微电影广告所面临的问…...
uniapp新手入门
前言: 这篇文章主要写的是uniapp的基础知识,可以让大家快速上手uniapp,同时避掉一些可能踩到的坑。 一. 什么是uniapp uniapp是由dcloud 公司开发的多端融合框架。uniapp的出现让我们的开发更为方便,一次开发,多端运行…...
linux segfault at 问题定位实践
问题:程序崩溃,打印为:app[13016]: segfault at 7fb668d29930 ip 00007fb668d3c23c sp 00007fb668e7de20 error 7 in mydefine.so[7fb668d3400011000]定位步骤:基础分析数据,大概了解反馈信息(根据chatGPT&…...
SpringCloud+SpringCloudAlibaba
架构的演进1.1单体架构将所有业务场景的表示层、业务逻辑层和数据访问层放在一个工程中,最终经过编译、打包,部署在一台服务器上。◆ 1.1.1单体架构的优点1)部署简单: 由于是完整的结构体,可以直接部署在一个服务器上即可。2&…...
华为OD机试 - 路灯照明(C 语言解题)【独家】
最近更新的博客 华为od 2023 | 什么是华为od,od 薪资待遇,od机试题清单华为OD机试真题大全,用 Python 解华为机试题 | 机试宝典【华为OD机试】全流程解析+经验分享,题型分享,防作弊指南)华为od机试,独家整理 已参加机试人员的实战技巧文章目录 使用说明本期题目:路灯照明…...
Linux程序替换
Linux程序替换创建子进程的目的?程序替换如何实现程序替换?什么是程序替换?先见一见单进程版本的程序替换程序替换原理多进程版本的程序替换execl函数组简易版Shell创建子进程的目的? 目的:为了帮助父进程完成一些特定的任务&…...
@JsonFormat @DataTimeFormat 时间格式
省流:用JsonFormat即可有时候会看到入参dto里,在时间类型的变量上用DateTimeFormat,代码如下。public class XXXdto{DateTimeFormat(pattern "yyyy-MM-dd hh:mm:ss")private Date startDate; }这是为了入参传日期格式的值。即前端…...
带你玩转modbusTCP通信
modbus TCP Modbus TCP是一种基于TCP/IP协议的Modbus通信协议,它是Modbus协议的一种变体,用于在以太网上进行通信。Modbus TCP协议是一种开放的通信协议,它支持多种编程语言和操作系统,并且可以在不同的硬件和软件平台上进行通信…...
2021牛客OI赛前集训营-提高组(第三场)T2交替
2021牛客OI赛前集训营-提高组(第三场) 题目大意 一个长度为nnn的数组aaa,每秒都会变成一个长度为n−1n-1n−1的新数组a′aa′,其变化规则如下 如果当前数组aaa的大小nnn为偶数,则对于新数组a′aa′的每一个位置i(1≤…...
论文投稿指南——中文核心期刊推荐(金融)
【前言】 🚀 想发论文怎么办?手把手教你论文如何投稿!那么,首先要搞懂投稿目标——论文期刊 🎄 在期刊论文的分布中,存在一种普遍现象:即对于某一特定的学科或专业来说,少数期刊所含…...
变量 varablie 声明- Rust 变量 let mut 声明与 C/C++ 变量声明对比分析
一、变量声明设计:let 与 mut 的哲学解析 Rust 采用 let 声明变量并通过 mut 显式标记可变性,这种设计体现了语言的核心哲学。以下是深度解析: 1.1 设计理念剖析 安全优先原则:默认不可变强制开发者明确声明意图 let x 5; …...
java调用dll出现unsatisfiedLinkError以及JNA和JNI的区别
UnsatisfiedLinkError 在对接硬件设备中,我们会遇到使用 java 调用 dll文件 的情况,此时大概率出现UnsatisfiedLinkError链接错误,原因可能有如下几种 类名错误包名错误方法名参数错误使用 JNI 协议调用,结果 dll 未实现 JNI 协…...
1.3 VSCode安装与环境配置
进入网址Visual Studio Code - Code Editing. Redefined下载.deb文件,然后打开终端,进入下载文件夹,键入命令 sudo dpkg -i code_1.100.3-1748872405_amd64.deb 在终端键入命令code即启动vscode 需要安装插件列表 1.Chinese简化 2.ros …...
【OSG学习笔记】Day 16: 骨骼动画与蒙皮(osgAnimation)
骨骼动画基础 骨骼动画是 3D 计算机图形中常用的技术,它通过以下两个主要组件实现角色动画。 骨骼系统 (Skeleton):由层级结构的骨头组成,类似于人体骨骼蒙皮 (Mesh Skinning):将模型网格顶点绑定到骨骼上,使骨骼移动…...
免费PDF转图片工具
免费PDF转图片工具 一款简单易用的PDF转图片工具,可以将PDF文件快速转换为高质量PNG图片。无需安装复杂的软件,也不需要在线上传文件,保护您的隐私。 工具截图 主要特点 🚀 快速转换:本地转换,无需等待上…...
如何应对敏捷转型中的团队阻力
应对敏捷转型中的团队阻力需要明确沟通敏捷转型目的、提升团队参与感、提供充分的培训与支持、逐步推进敏捷实践、建立清晰的奖励和反馈机制。其中,明确沟通敏捷转型目的尤为关键,团队成员只有清晰理解转型背后的原因和利益,才能降低对变化的…...
Leetcode33( 搜索旋转排序数组)
题目表述 整数数组 nums 按升序排列,数组中的值 互不相同 。 在传递给函数之前,nums 在预先未知的某个下标 k(0 < k < nums.length)上进行了 旋转,使数组变为 [nums[k], nums[k1], …, nums[n-1], nums[0], nu…...
认识CMake并使用CMake构建自己的第一个项目
1.CMake的作用和优势 跨平台支持:CMake支持多种操作系统和编译器,使用同一份构建配置可以在不同的环境中使用 简化配置:通过CMakeLists.txt文件,用户可以定义项目结构、依赖项、编译选项等,无需手动编写复杂的构建脚本…...
DeepSeek源码深度解析 × 华为仓颉语言编程精粹——从MoE架构到全场景开发生态
前言 在人工智能技术飞速发展的今天,深度学习与大模型技术已成为推动行业变革的核心驱动力,而高效、灵活的开发工具与编程语言则为技术创新提供了重要支撑。本书以两大前沿技术领域为核心,系统性地呈现了两部深度技术著作的精华:…...
若依登录用户名和密码加密
/*** 获取公钥:前端用来密码加密* return*/GetMapping("/getPublicKey")public RSAUtil.RSAKeyPair getPublicKey() {return RSAUtil.rsaKeyPair();}新建RSAUti.Java package com.ruoyi.common.utils;import org.apache.commons.codec.binary.Base64; im…...
