编写一个函数, 接受一个Promise数组, 通过参数控制最大并发数, 所有任务结束后通过回调得到最终结果。直接上代码, 逻辑都在注释里解释清楚了。
function run(list, num, callback) {
if (num === 0 || num >= list.length) {
// 如果并发数设置为 0 或者大于当前任务数, 直接使用 allSettled 全部执行
const tasks = list.map(ele => ele())
Promise.allSettled(tasks).then(res => {
callback(res)
})
return
}
const results = [] // 所有任务的结果, 需要保证顺序与 list 中任务顺序相同
let runningTasks = 0 // 当前运行中的任务
/**
* 启动一个任务
*/
const next = () => {
let len = list.length
if (!len) {
return // 没有待启动的任务时直接返回
}
// 取出一个任务, 并且记录其在 list 中的索引
let index = len - 1
let task = list.pop()
// 启动任务, 返回值仿照 allSettled
task().then(res => {
results[index] = {
status: 'fulfilled',
value: res
}
}).catch(err => {
results[index] = {
status: 'rejected',
reason: err
}
}).finally(() => {
runningTasks-- // 完成一个任务的时候减一
next() // 结束一个任务后再启动一个任务
if (runningTasks === 0) {
callback(results) // 如果运行中的任务数为 0 ,则表示所有任务都结束了, 使用回调传出结果
}
})
runningTasks++ // 启动一个任务的时候加一
if (runningTasks < num) {
// 初次运行时, 如果当前运行中的任务数小于并发数, 需要补齐启动的任务数到 num
next()
}
}
next() // 启动任务
}
let tasks = new Array(10).fill(0).map((ele, index) => {
return () => new Promise((resolve, reject) => {
console.log(`running ${index}`)
setTimeout(() => {
console.log(`done ${index}`)
if (Math.random() > 0.5) {
resolve(`p${index}`)
} else {
reject(`p${index}`)
}
}, Math.random() * 3000)
})
})
run(tasks, 3, console.log)
运行结果:
// running 9
// running 8
// running 7
// done 7
// running 6
// done 8
// running 5
// done 9
// running 4
// done 5
// running 3
// done 6
// running 2
// done 4
// running 1
// done 2
// running 0
// done 3
// done 1
// done 0
// [
// { status: 'fulfilled', value: 'p0' },
// { status: 'fulfilled', value: 'p1' },
// { status: 'rejected', reason: 'p2' },
// { status: 'fulfilled', value: 'p3' },
// { status: 'rejected', reason: 'p4' },
// { status: 'fulfilled', value: 'p5' },
// { status: 'fulfilled', value: 'p6' },
// { status: 'fulfilled', value: 'p7' },
// { status: 'rejected', reason: 'p8' },
// { status: 'fulfilled', value: 'p9' }
// ]