题解Promise并发控制
https://juejin.cn/post/6916317088521027598
https://juejin.cn/post/6916317088521027598
Promise
1. 理解
Promise是异步编程的一种解决方案。
Promise 是一个构造函数,接收一个函数作为参数,返回一个 Promise 实例
Promise的实例有三个状态:
- Pending(进行中)
- Resolved(已完成)
- Rejected(已拒绝)
Promise的实例有两个过程: - pending -> fulfilled : Resolved(已完成)
- pending -> rejected:Rejected(已拒绝)
注意:一旦从进行状态变成为其他状态就永远不能更改状态了。
注意:在构造 Promise 的时候,构造函数内部的代码是立即执行的。
2.方法
Promise有五个常用的方法:then()、catch()、all()、race()、finally。
- then()
then方法可以接受两个回调函数作为参数。第一个回调函数是Promise对象的状态变为resolved时调用,第二个回调函数是Promise对象的状态变为rejected时调用。其中第二个参数可以省略。
then方法返回的是一个新的Promise实例(不是原来那个Promise实例)。因此可以采用链式写法,即then方法后面再调用另一个then方法。 - catch()
该方法相当于then方法的第二个参数,指向reject的回调函数。不过catch方法还有一个作用,就是在执行resolve回调函数时,如果出现错误,抛出异常,不会停止运行,而是进入catch方法中。 - all()
all方法可以完成并行任务, 它接收一个数组,数组的每一项都是一个promise对象。当数组中所有的promise的状态都达到resolved的时候,all方法的状态就会变成resolved,如果有一个状态变成了rejected,那么all方法的状态就会变成rejected。 - race()
race方法和all一样,接受的参数是一个每项都是promise的数组,但是与all不同的是,当最先执行完的事件执行完之后,就直接返回该promise对象的值。如果第一个promise对象状态变成resolved,那自身的状态变成了resolved;反之第一个promise变成rejected,那自身状态就会变成rejected。 - finally()
finally方法用于指定不管 Promise 对象最后状态如何,都会执行的操作。该方法是 ES2018 引入标准的。
3. async/await
async是“异步”的简写,await则为等待,所以很好理解async 用于申明一个 function 是异步的,而 await 用于等待一个异步方法执行完成。
异常捕获
async function fn(){
try{
let a = await Promise.reject('error')
}catch(error){
console.log(error)
}
}
async/await对比Promise的优势
- 代码读起来更加同步,Promise虽然摆脱了回调地狱,但是then的链式调?也会带来额外的阅读负担
- Promise传递中间值?常麻烦,?async/await?乎是同步的写法,?常优雅
- 错误处理友好,async/await可以?成熟的try/catch,Promise的错误捕获?常冗余
- 调试友好,Promise的调试很差,由于没有代码块,你不能在?个返回表达式的箭头函数中设置断点,如果你在?个.then代码块中使?调试器的步进(step-over)功能,调试器并不会进?后续的.then代码块,因为调试器只能跟踪同步代码的每?步。
4. 题目
实现一个批量请求函数 multiRequest(urls, maxNum),要求如下:
? 要求最大并发数 maxNum
? 每当有一个请求返回,就留下一个空位,可以增加新的请求
? 所有请求完成后,结果按照 urls 里面的顺序依次打出
5. 解答
// 整体采用递归调用来实现:最初发送的请求数量上限为允许的最大值,并且这些请求中的每一个都应该在完成时继续递归发送,通过传入的索引来确定了urls里面具体是那个URL,保证最后输出的顺序不会乱,而是依次输出。
function multiRequest(urls = [], maxNum) {
// 请求总数量
const len = urls.length;
// 根据请求数量创建一个数组来保存请求的结果
const result = new Array(len).fill(false);
// 当前完成的数量
let count = 0;
return new Promise((resolve, reject) => {
// 请求maxNum个
while (count < maxNum) {
next();
}
function next() {
let current = count++;
// 处理边界条件
if (current >= len) {
// 请求全部完成就将promise置为成功状态, 然后将result作为promise值返回
!result.includes(false) && resolve(result);
return;
}
const url = urls[current];
console.log(`开始 ${current}`, new Date().toLocaleString());
fetch(url)
.then((res) => {
// 保存请求结果
result[current] = res;
console.log(`完成 ${current}`, new Date().toLocaleString());
// 请求没有全部完成, 就递归
if (current < len) {
next();
}
})
.catch((err) => {
console.log(`结束 ${current}`, new Date().toLocaleString());
result[current] = err;
// 请求没有全部完成, 就递归
if (current < len) {
next();
}
});
}
});
}