网站首页 > 知识剖析 正文
什么是并发?
因为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
}, [])
因此也可以在页面中找到一些可优化的请求,转换为并行,对于首屏渲染的优化是有很大帮助的。
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.2s
console.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.2s
console.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.2s
console.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.8s
console.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.8s
console.log(data1, data2); // request1 end, request2 end
}
syncFn();
总结
- 如果是在安装了axios或者其它请求库,在这些库本身提供支持并发的api情况下,如axios.all、axios.spread,建议使用他们提供的这些api,因为作为一个百万级下载量的库,提供的这些api考虑到的边界条件、兼容性肯定是比其它原生方法好用的。
- 在没有这些库的情况下,推荐使用Promise.allSettled,如果你喜欢用 async、await 也可以,结果没有区别,看个人喜好了。但是在我看来在处理并发的情况下 async、await的写法感觉就不那么简洁了。
猜你喜欢
- 2024-11-17 CSV Parquet Avro:为正确的工作选择合适的工具
- 2024-11-17 JS基础进阶- 同步异步编程和EventLoop底层机制
- 2024-11-17 5个你应该知道的JavaScript技巧,不能错过!
- 2024-11-17 map映射+异步加载 完美过渡 if else
- 2024-11-17 Chrome控制台的一些有用API(chrome控件)
- 2024-11-17 Javascript调试器自编代码及运用(js调试工具和方法如何使用)
- 2024-11-17 NET中的定时器:种类与应用场景(winform定时器)
- 2024-11-17 全栈之路:从一个深拷贝开始循序渐进
- 2024-11-17 localStorage灵魂五问。 5M??10M(灵魂官方网站)
- 2024-11-17 JS中用于跟踪程序执行时间的专用函数,两个同时出现截断时间戳
- 最近发表
- 标签列表
-
- xml (46)
- css animation (57)
- array_slice (60)
- htmlspecialchars (54)
- position: absolute (54)
- datediff函数 (47)
- array_pop (49)
- jsmap (52)
- toggleclass (43)
- console.time (63)
- .sql (41)
- ahref (40)
- js json.parse (59)
- html复选框 (60)
- css 透明 (44)
- css 颜色 (47)
- php replace (41)
- css nth-child (48)
- min-height (40)
- xml schema (44)
- css 最后一个元素 (46)
- location.origin (44)
- table border (49)
- html tr (40)
- video controls (49)