当我们需要保证代码在多个异步操作都完成后执行,通常我们会使用Promise.all 来实现。以请求多张图片为例:
// 为了演示方便,我们在此用fetchImage函数来模拟异步请求图片,返回成功提示
function fetchImage(url) {
// 模拟请求的响应时间在0 - 1s之间随机
const timeCost = Math.random() * 1000
return new Promise(resolve => setTimeout(resolve, timeCost, 'get: ' + url))
}
// 待请求的图片
const imageUrls = [
'pic_1.png',
'pic_2.png',
'pic_3.png',
'pic_4.png',
'pic_5.png',
'pic_6.png',
]
Promise
.all(imageUrls.map(url => fetchImage(url)))
.then(resList => console.log(resList))
输出为
[
"get: pic_1.png",
"get: pic_2.png",
"get: pic_3.png",
"get: pic_4.png",
"get: pic_5.png",
"get: pic_6.png"
]
如果我们对并行的请求数量有限制,Promise.all
自身是不具有这个功能的。
那么接下来,我们依旧以上述的fetchImage
为例,来实现一个可以对图片请求进行并行限制的函数:
/**
* @description 带并发限制的图片并发请求
* @param {Array} imageUrls 待请求的图片url列表
* @param {Object} limit 最大并发个数限制
* @return { Promise } resList
*/
function fetchImageWithLimit(imageUrls, limit = 4)
本文提供两个思路解决该问题:
Promise.all
来实现Promise.race
来实现步骤如下:
limit
个Promise对象,作为Promise.all
的参数imageUrls
中取出一个url进行请求,若无则resolvefunction fetchImageWithLimit(imageUrls, limit) {
// copy一份,作为剩余url的记录
let urls =[ ...imageUrls ]
// 用来记录url - response 的映射
// 保证输出列表与输入顺序一致
let rs = new Map()
// 递归的去取url进行请求
function run() {
if(urls.length > 0) {
// 取一个,便少一个
const url = urls.shift()
// console.log(url, ' [start at] ', ( new Date()).getTime() % 10000)
return fetchImage(url).then(res => {
// console.log(url, ' [end at] ', ( new Date()).getTime() % 10000)
rs.set(url, res)
return run()
})
}
}
// 当imageUrls.length < limit的时候,我们也没有必要去创建多余的Promise
const promiseList = Array(Math.min(limit, imageUrls.length))
// 这里用Array.protetype.fill做了简写,但不能进一步简写成.fill(run())
.fill(Promise.resolve())
.map(promise => promise.then(run))
return Promise.all(promiseList).then(() => imageUrls.map(item => rs.get(item)))
}
去掉代码中console.log
,可展示出如下结果:
fetchImageWithLimit(imageUrls, 2)
.then(res => console.log(res))
.catch(err => console.error(err))
// 过程中输出依次为(次序每次都可能不一样)
pic_1.png [start at] 746
pic_2.png [start at] 746
pic_1.png [end at] 814
pic_3.png [start at] 814
pic_3.png [end at] 1329
pic_4.png [start at] 1330
pic_2.png [end at] 1460
pic_5.png [start at] 1460
pic_4.png [end at] 2172
pic_6.png [start at] 2172
pic_5.png [end at] 2401
pic_6.png [end at] 2968
// 最终的结果输出
[
"get: pic_1.png",
"get: pic_2.png",
"get: pic_3.png",
"get: pic_4.png",
"get: pic_5.png",
"get: pic_6.png"
]
步骤如下:
limit
个图片请求,作为Promise.race
的参数limit - 1
个图片请求一起作为新一轮的Promise.race
的参数具体可参考 这篇博客中对于asyncPool
实现的分析。
我觉得写的挺好,有空了我再来自己手撕一遍。~。~