1.概念
Promise对象的状态不受外界影响 (3种状态)
- Pending状态(进行中)
- Fulfilled状态(已成功)
- Rejected状态(已失败)
Promise.race
- 那个返回快就返回那个promise,无论返回结果是成功还是失败。 注意点:
- 虽然已经返回最快的,但是其他的promise依然会继续进行。
- 传入参数promiseList集合可以是之前已经发起的,即可以重复继续传入Promise.race调用。
let promiseList = [pro1,pro2,pro3];
Promise.race(promiseList).then((ret) => {
//1.假设pro2先结束,这时候pro1和pro3依然正在进行中
//2.依然可以继续使用Promise.race 处理剩下的[pro1,pro3]
let promiseList2 = [pro1,pro3];
Promise.race(promiseList2).then((ret) => {
...
})
})
Promise.all
- 把所有pormise成功执行完才返回对应所有集合的状态。但是如果遇到一个reject错误,就直接返回reject一个错误,不等其他。
for of
- 是一个用于循环迭代可迭代的对象或集合,循环的时候可以动态给数据组移除或添加,动态变化循环。
2.asyncPool 学习
es7版
逻辑思想:
- 通过一个动态的executing 实时保存着固定长度的 promise队列,每次通过同步 Promise.race抢出一个promise后,再加入一个,再Promise.race抢出一个,一直循环下去。
- 最后再Promise.all的时候只剩下 poolLimit以内状态还是Pending 的内容在一起promise.all一起执行一遍,并等待结果返回所有成功数组。
- executing存储的是实时运行的队列,一般只会小于等于poolLimit 限制的长度。
- ret 记录所有promise的队列,用于最后把所有结果返回给用户。
缺点:
- 如果限制条数过大,则可能promise.all的数量会很多
- 如果遇到reject时候,不能发起重连,只能在结束后,再补发。
//poolLimit 限制并发的长度
//array 实际需要运行的所有任务集合
//iteratorFn 每一个任务需要执行的异步方法,返回promise对象
async function asyncPool(poolLimit, array, iteratorFn) {
const ret = []; // 存储所有的异步任务
const executing = []; // 存储正在执行的异步任务
for (const item of array) {
// 调用iteratorFn函数创建异步任务,
//这里用于解决promise.all 遇到reject只返回一个错误并中断问题。每个请求都是 resolve,确保promise.all能够全部执行完毕。
const p = Promise.resolve().then(() => iteratorFn(item, array));
ret.push(p); // 保存新的异步任务,记录所有promise的信息
// 当poolLimit值小于或等于总任务个数时,进行并发控制
if (poolLimit <= array.length) {
// 当任务完成后,从正在执行的任务数组中移除已完成的任务
const e = p.then(() => {
executing.splice(executing.indexOf(e), 1)
});
executing.push(e); // 保存正在执行的异步任务
if (executing.length >= poolLimit) { //当实际运行的长度 满足 限制的长度开始竞赛请求,注意这里里面可能包含之前已经处理一段时间的promise
await Promise.race(executing); // 等待较快的任务执行完成,这里响应最快结束的一个promise,然后继续循环加入。
}
}
}
return Promise.all(ret);//到这里其实大部分promise已经都是Fulfilled,所有会直接返回 promise.all 把剩下 还是Pending 再执行一下,并等待结果返回所有成功数组。
}
//测试代码
const timeout = i => new Promise(resolve => setTimeout(() => {
console.log("resolve",i)
resolve(i)
}, i));
async function test() {
let a ;
try {
a = await asyncPool(2, [1000, 5000, 3000, 2000], timeout);
} catch (error) {
console.log("error",error)
}
console.log("ret",a)
}
test()
//打印结果
//resolve 1000
//resolve 3000
//resolve 5000
//resolve 2000
//ret [ 1000, 5000, 3000, 2000 ]
执行时长逻辑
- 0秒时候,一次只有2个任务并行,把1000 和 3000 任务加入队列
- 到第1秒时,第一个1000 结束,打印并移出,第二个3000 继续运行,同时加入新任务5000
- 到第3秒时,任务3000 结束,打印并移出,同时加入新任务2000
- 到第6秒时,任务5000 结束,打印并移出。
- 到第7秒时,任务2000 结束,打印并移出。
代码执行流程:

executing的promise数组的整个变化过程:
es6版
逻辑思想:
- 通过封装enqueue(),从i==0开始调用,当executing长度满足poolLimit 条件时,执行Promise.race,在executing取出最先返回的promise,在promise.then之后再递归重复enqueue()方法。直到i变量等于实际总长度,跳出循环。(由于es6没有async await ,只能通过地狱回调来实现)
function asyncPool(poolLimit, array, iteratorFn) {
let i = 0;
const ret = []; // 存储所有的异步任务
const executing = []; // 存储正在执行的异步任务
const enqueue = function () {
if (i === array.length) {
return Promise.resolve();
}
const item = array[i++]; // 获取新的任务项
const p = Promise.resolve().then(() => iteratorFn(item, array));
ret.push(p);
let r = Promise.resolve();
// 当poolLimit值小于或等于总任务个数时,进行并发控制
if (poolLimit <= array.length) {
// 当任务完成后,从正在执行的任务数组中移除已完成的任务
const e = p.then(() => executing.splice(executing.indexOf(e), 1));
executing.push(e);
if (executing.length >= poolLimit) {
r = Promise.race(executing);
}
}
// 正在执行任务列表 中较快的任务执行完成之后,才会从array数组中获取新的待办任务
return r.then(() => enqueue());
};
return enqueue().then(() => Promise.all(ret));
}
//测试代码
const timeout = i => new Promise((resolve,reject) => setTimeout(() => {
console.log("resolve",i)
resolve(i)
}, i));
asyncPool(2, [1000, 5000, 3000, 2000], timeout).then((ret) => {
console.log("ret",ret)
})
//resolve 1000
//resolve 3000
//resolve 5000
//resolve 2000
//ret [ 1000, 5000, 3000, 2000 ]
3.递归实现并发控制 es7
3.1简单版
原理: 通过while创建多个任务,每个任务都执行start()方法,start方法会一直通过 shift()方法拿到array列表最前面一条数据执行。每执行完一个任务就把counter++,当counter等于array原来长度 则代表所有并发已经结束。
async function sendRequest(limit=4,array,uploadReqFn){
return new Promise((resolve,reject)=>{
const len = array.length
let counter = 0
let ret = [] //记录所有promise情况
const start = async ()=>{
const task = array.shift()
if(task){
ret.push(task)
await Promise.resolve().then( (res) => {
return uploadReqFn(task)
})
if(counter==len-1){
// 最后一个任务
resolve(Promise.all(ret)) // 同时把所有promise的执行结果都返回
}else{
counter++
// 启动下一个任务
start()
}
}
}
//通过while创建多个(默认4个)固定的递归循环,start方法会一直递归start方法不断请求。
while(limit>0){
start()
limit-=1
}
})
}
const timeout = i => new Promise((resolve,reject) => setTimeout(() => {
console.log("resolve",i)
resolve(i)
}, i));
async function test() {
try {
let a = await sendRequest(2, [1000, 5000, 3000, 2000], timeout);
console.log("ret",a)
} catch (error) {
console.log("error",error)
}
}
test()
//输出
//resolve 1000
//resolve 3000
//resolve 5000
//resolve 2000
//ret [ 1000, 5000, 3000, 2000 ]
3.2兼容错误异常处理版
当遇到一个start任务失败时自动重新发起,利用unshift优先插入错误重连的任务。 当超过4次,则当前的所有start请求都终止执行。
async function sendRequest(limit=4,array,uploadReqFn){
return new Promise((resolve,reject)=>{
const len = array.length
let counter = 0
let isStop = false
let ret = [] //记录所有promise情况
const start = async ()=>{
if(isStop){
return
}
const task = array.shift()
if(task){
ret.push(task)
try{
await Promise.resolve().then( (res) => {
return uploadReqFn(task.time)
})
if(counter==len-1){
// 最后一个任务
resolve(Promise.all(ret)) // 同时把所有promise的执行结果都返回
}else{
counter++
// 启动下一个任务
start()
}
}catch(e){
if(task.error<3){
task.error++
array.unshift(task)
start()
}else{
// 错误三次
isStop = true
reject(Promise.all(ret))
}
}
}
}
//通过while创建多个(默认4个)固定的递归循环,start方法会一直递归start方法不断请求。
while(limit>0){
start()
limit-=1
}
})
}
const timeout = i => new Promise((resolve,reject) => setTimeout(() => {
if (Math.random()> 0.3) {
console.log("resolve",i)
resolve(i)
} else {
console.log("reject",i)
reject(i)
}
}, i));
async function test() {
try {
let taskList = [
{error:0,time:1000},
{error:0,time:5000},
{error:0,time:3000},
{error:0,time:2000}
]
let a = await sendRequest(2, taskList, timeout);
console.log("ret",a)
} catch (error) {
console.log("error",error)
}
}
test()
//输出 这里3000 发生了一次异常错误,然后重新发起了请求
//resolve 1000
//reject 3000
//resolve 5000
//resolve 3000
//resolve 2000
//ret [ { error: 0, time: 1000 },
// { error: 0, time: 5000 },
// { error: 1, time: 3000 },
// { error: 1, time: 3000 },
// { error: 0, time: 2000 } ]
4.递归实现并发控制 es6版
逻辑思想: 通过while创建多个任务,每个任务都执行start()方法,start方法会一直通过count++往下不断请求。当遇到错误,则传入对应的错误索引继续请求。如:start(3) 重新发起3对应的请求
function sendRequest(limit=4,array,uploadReqFn){
const len = array.length
const result = new Array(len).fill(false)
let count = 0
// 设置 error,记录错误的次数
return new Promise((resolve, reject) => {
while(count < limit){
start()
}
function start(current = ''){
if(current === ''){
current = count;
count++;
}
if (current >= len) {//当游标到达实际的 集合总长度
// 请求全部完成就将promise置为成功状态, 然后将result作为promise值返回
!result.includes(false) && resolve(result);
return;
}
array[current].error++
uploadReqFn(array[current]).then((res) => {
result[current] = res;
// 请求没有全部完成, 就递归
if (current < len) {
start();
}
}).catch((err) => {
// 报错时,请求次数小于3时将重新发起请求
if(array[current].error < 3){
start(current)
}else{
result[current] = err;
// 请求没有全部完成, 就递归
if (current < len) {
start();
}
}
});
}
})
}
const timeout = i => new Promise((resolve,reject) => setTimeout(() => {
if (Math.random()> 0.5) {
console.log("resolve",i)
resolve(i)
} else {
console.log("reject",i)
reject(i)
}
}, i));
async function test() {
let a ;
try {
let taskList = [
{error:0,time:1000},
{error:0,time:5000},
{error:0,time:3000},
{error:0,time:2000}
]
a = await sendRequest(2, taskList, timeout);
} catch (error) {
console.log("error",error)
}
console.log("ret",a)
}
test()
//输出 这里 2000 发生了错误,并发起重连
//resolve { error: 1, time: 1000 }
//resolve { error: 1, time: 5000 }
//resolve { error: 1, time: 3000 }
//reject { error: 1, time: 2000 }
//resolve { error: 2, time: 2000 }
//ret [ { error: 1, time: 1000 },
// { error: 1, time: 5000 },
// { error: 1, time: 3000 },
// { error: 2, time: 2000 } ]
4.2.递归实现2 class
class SuperTask {
constructor(paralleCnt = 2){
this.tasks = []
this.runningCnt = 0
this.paralleCnt = paralleCnt
}
add(task){
return new Promise((resolve,reject) => {
this.tasks.push({task,resolve,reject})
// console.log("this.tasks",this.tasks)
this._run()
})
}
_run(){
while(this.runningCnt <this.paralleCnt && this.tasks.length) {
// console.log("xxx")
const {task,resolve,reject} = this.tasks.shift()
// console.log(task,resolve,reject)
this.runningCnt++;
task().then(resolve,reject).finally(() => {
this.runningCnt--;
this._run()
})
}
}
}
const superTask = new SuperTask(2)
function addTask(time,name){
superTask.add(() =>
timeout(time) ).then(() => {
console.log(`${name}任务被执行`)
})
}
function timeout(time){
return new Promise(resolve => {
setTimeout(() => {
resolve(time)
}, time);
})
}
addTask(1000,1)
addTask(5000,2)
addTask(2000,3)
addTask(3000,4)
addTask(1000,5)
5.Promise.all 处理reject问题
核心思想: 把集合里的promise 再包一层,默认都是返回resolve,再对请求返回reject信息进行处理,通过对象的属性描述是否异常。
代码实现
封装promiseWithError方法
const p1 = new Promise((resolve, reject) => {
setTimeout(() => {
resolve('promise resolve 1');
}, 1000);
});
const p2 = new Promise((resolve, reject) => {
setTimeout(() => {
resolve('promise resolve 2');
}, 2000);
});
const p3 = new Promise((resolve, reject) => {
setTimeout(() => {
reject('promise reject 3');
}, 3000);
});
Promise.all([p1, p2, p3])
.then(res => {
console.log('resolve:', res);
})
.catch(err => {
console.log('reject:', err);
});
// 最终输出为: promise reject 3
//定义异常捕获的promise方法
async function promiseWithError(p) {
try {
const res = await p;
return {
err: 0,
data: res
};
} catch(e) {
return {
err: 1
}
}
}
//先转化一次promise,让所有的promise默认都是resolve,错误信息通过对象属性描述。
Promise.all([p1, p2, p3].map(item => promiseWithError(item)))
.then(res => {
console.log('resolve:', res);
})
.catch(err => {
console.log('reject:', err);
});
// 最终的输出为:
// resolve: [{ err: 0, data: "promise resolve 1"}, { err: 0, data:"promise resolve 2"}, { err: 1 }]