前端处理并发的最佳实践
创始人
2024-05-31 05:29:57
0

什么是并发?

因为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); 
})

总结

  1. Promise.all在处理并发时,如果有一个promise失败,就不会走.then, 但是如果只是需要在所有异步执行完成之后去执行其它副作用代码,可以把代码写在.finally中实现。

  2. 但是如果需要在有异步失败之后,获取到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.allSettledPromise.all没有太大区别,只是对于catch的情况下做了一次弥补。

同时Promise.allSettled的兼容性并没有Promise.all来得好。

总结

  1. Promise.allSettled在处理并发时,不怕异步执行失败,继续会走.then,完美解决Promise.all在并发有失败情况下,拿不到值的情况

  2. 唯一的缺点,比起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();

总结

  1. 如果是在安装了axios或者其它请求库,在这些库本身提供支持并发的api情况下,如axios.all、axios.spread,建议使用他们提供的这些api,因为作为一个百万级下载量的库,提供的这些api考虑到的边界条件、兼容性肯定是比其它原生方法好用的。

  2. 在没有这些库的情况下,推荐使用Promise.allSettled,如果你喜欢用 async、await 也可以,结果没有区别,看个人喜好了。但是在我看来在处理并发的情况下 async、await的写法感觉就不那么简洁了。

相关内容

热门资讯

我的家初一作文800字【经典... 我的家初一作文800字 篇一我们家是一个温馨而幸福的小家庭。爸爸是一个高大而慈祥的人,他是家里的顶梁...
照亮作文600字【优选5篇】 照亮作文600字 篇一照亮作文现代社会,写作已经成为了我们生活中不可或缺的一部分。无论是在学校还是在...
办好G20做好东道主初一作文... 办好G20做好东道主初一作文 篇一G20峰会是全球最重要的经济论坛之一,举办这一峰会对于中国来说是一...
团结的力量初一作文(经典5篇... 团结的力量初一作文 篇一团结的力量团结的力量是一种强大的力量,它能够将不同的个体聚集在一起,从而共同...
难忘那慈爱的眼神初中作文【精... 难忘那慈爱的眼神初中作文 篇一初中时,我曾经遇到过一位让我难以忘怀的老师,她独特的慈爱眼神给我留下了...
感恩母亲初中作文600字(经... 篇一:感恩母亲的教导母亲,是我生命中最重要的人。她是我生活的向导,她是我成长的引路人。在我初中的时候...
初一那一次真难忘500字作文... 初一那一次真难忘500字作文 篇一初一那一天,我怀着激动而紧张的心情来到了新学校。这是我人生中的一个...
我喜欢的格言 我喜欢的一则格言  我最喜欢的一句名言是:最大的幸福是在于我们的缺点得到纠正和我们的错误得到补救。这...
月亮初一500字作文(优质6... 月亮初一500字作文 篇一月亮初一,是指每个月的初一日晚上,当月亮初升时的美丽景象。在这个特殊的时刻...
乐在其中初中作文【经典6篇】 乐在其中初中作文 篇一我最喜欢的运动——篮球篮球是一项非常受欢迎的运动,也是我最喜欢的运动之一。每当...
初中我的奶奶作文(精选6篇) 初中我的奶奶作文 篇一我亲爱的奶奶奶奶是我最亲近的人,她矮矮胖胖的身材让人忍不住想拥抱她。她总是笑容...
七秒钟的记忆初中作文【优质3... 七秒钟的记忆初中作文 篇一七秒钟的记忆我曾经读过一本书,书名叫做《七秒钟的记忆》。这本书给了我很大的...
当青春期遇上更年期500字作... 当青春期遇上更年期500字作文 篇一:青春期与更年期的共同点与差异青春期和更年期是人生中的两个重要阶...
我的表姐初中优秀作文【精简6... 我的表姐初中优秀作文 篇一初中生活的收获和成长初中三年,对我来说是充满挑战和成长的三年。在这个阶段,...
初一感恩母校征文范文【精简6... 初一感恩母校征文范文 篇一初一感恩母校初一的日子,对于我们来说,是充满了新鲜和挑战的。离开了小学的温...
初中英语作文题【推荐3篇】 初中英语作文题 篇一My Favorite HobbyHobbies play an importa...
初中记叙文:那双紧握的手【推... 初中记叙文:那双紧握的手 篇一我记得那是一个寒冷的冬日,我和妈妈一起去参加一个社区活动。活动结束后,...
父亲初一作文【精简6篇】 父亲初一作文 篇一:我和爸爸的足球之旅爸爸是我的英雄,他是一位充满活力和热情的足球迷。每当有重要的足...
初一作文我的爱好800字记叙... 初一作文我的爱好800字记叙文 第一篇大家好!我是一个非常活泼可爱,并且爱好广泛的小女孩。我的业余爱...
最底层的人初一作文【推荐5篇... 最底层的人初一作文 篇一最底层的人初一作文我是一个来自最底层的人,我是一个农村的孩子。在我心中,最底...