1、什么是并发?
因为js是单线程的,所以前端的并发指的是在极短时间内发送多个数据请求,比如说循环中发送ajax。
2、Promise.all优缺点,.then,.finally的区别, 如果其中有promise走了reject,能不能获取到其它resolve的promise传递过来的值?
- 用Promise.all处理并发, 当所有promise全部成功时, 会走.then,并且可以拿到所有promise中传进resolve中的值
let p1 = new Promise((resolve, reject) => {
setTimeout(() => {
resolve([1, 2, 3])
}, 1000);
})
let p2 = new Promise((resolve, reject) => {
setTimeout(() => {
resolve([4, 5, 6])
}, 3000);
})
console.time();
Promise.all([p1, p2])
.then(result => {
console.timeEnd();
console.log(result);
})
- 用Promise.all处理并发, 当其中有一个promise执行失败, 不会走.then, 会走finally(不管成功失败都会执行), 但是因为finally回调函数中,无参数,所有拿不到执行成功的promise传进resolve的值
let p1 = new Promise((resolve, reject) => {
setTimeout(() => {
resolve([1, 2, 3]);
}, 1000);
})
let p2 = new Promise((resolve, reject) => {
setTimeout(() => {
reject([4, 5, 6]);
}, 3000);
})
console.time();
Promise.all([p1, p2])
.then(result => {
console.timeEnd();
console.log(result);
})
.finally(() => {
console.timeEnd();
})
总结:
- Promise.all在处理并发时,如果有一个promise失败,就不会走.then, 但是如果只是需要在所有异步执行完成之后去执行其它副作用代码,可以把代码写在.finally中实现。
- 但是如果需要在有异步失败之后,获取到promise实例通过resolve传过来的值,则不行
3、Promise.allSettled
- 在promise.all中实现不了的功能, 可以使用promise.allSettled来实现, 在promise.allSettled中,当其中有一个promise执行失败时,会继续走.then, 并且可以同时拿到传给resolve、reject的值
let p1 = new Promise((resolve, reject) => {
setTimeout(() => {
resolve([1])
}, 1000);
})
let p2 = new Promise((resolve, reject) => {
setTimeout(() => {
reject([2])
}, 3000);
})
console.time();
Promise.allSettled([p1, p2])
.then(result => {
console.timeEnd();
console.log(result);
})
总结:
- Promise.allSettled在处理并发时,不怕异步执行失败,继续会走.then,完美解决promise.all在并发有失败情况下,拿不到值的情况
- 唯一的缺点,比起promise.all兼容性差一点,多了两个不支持的浏览器, 安卓端火狐浏览器(Firefox for Android)及 Samsung Internet 浏览器。
4、async、await优缺点,及async、await能处理并发吗?怎么写才能让程序不阻塞?
- 说起处理异步的方法,肯定要说一下async、await, async、await写法相比之下语法更优雅,代码可读性更高。 但是当要处理并发的时候,能不能用async、await出来? 会不会造成程序阻塞?看下下面这个例子。
let getData1 = () => {
return new Promise((resolve, reject) => {
setTimeout(() => {
resolve([1, 2, 3])
}, 2000);
})
};
let getData2 = () => {
return new Promise((resolve, reject) => {
setTimeout(() => {
resolve([4, 5, 6])
}, 3000);
})
}
let syncFn = async () => {
console.time();
const data1 = await getData1();
const data2 = await getData2();
console.timeEnd();
console.log(data1, data2);
}
syncFn();
- 上面那种写法,最终能拿到两个异步的值, 但是发生了阻塞,总的执行时间是两个请求加起来的时间,曾经一度以为,async、await的优点就是代码优雅、可读性高等,缺点就是会造成程序的阻塞,并不适合处理并发。
- 但是在之前有一段时间,和一个比较厉害的朋友,一起写了一个koa2的项目,(koa2框架使用Babel实现Async方法,可以在node环境中使用async、await语法,所以在koa2中很推崇使用async、await来处理异步),所以当提及这个问题的时候,他给我指点了迷津,代码如下:
let getData1 = () => {
return new Promise((resolve, reject) => {
setTimeout(() => {
resolve([1, 2, 3])
}, 2000);
})
};
let getData2 = () => {
return new Promise((resolve, reject) => {
setTimeout(() => {
resolve([4, 5, 6])
}, 3000);
})
}
let syncFn = async () => {
console.time();
const p1 = getData1();
const p2 = getData2();
const data1 = await p1;
const data2 = await p2;
console.timeEnd();
console.log(data1, data2);
}
syncFn();
个人总结(有错误请指正):
1、如果是在安装了axios或者其它请求库,在这些库本身提供支持并发的api情况下,如axios.all、axios.spread,建议使用他们提供的这些api,因为作为一个百万级下载量的库,提供的这些api考虑到的边界条件、兼容性肯定是比其它原生方法好用的。
2、在没有这些库的情况下,推荐使用Promise.allSettled,如果你喜欢用 async、await 也可以,结果没有区别,看个人喜好了。但是在我看来在处理并发的情况下 async、await的写法感觉就不那么简洁了。