前言
前几天看了作者 zz_jesse 的 写给新手前端的各种文件上传攻略,从小图片到大文件断点续传 ,学习了很多有关上传的知识点。但在大文件分片上传一块,作者有提及分片上传需要做并发限制处理,但他的demo并没有做。抱着学习的心态,我又去网上学习了一番。。
Promise.all()
Promise.all() 方法用于将多个 Promise 实例,包装成一个新的 Promise 实例。
const p = Promise.all([p1, p2, p3]);
上面代码中, Promise.all() 方法接受一个数组作为参数,p1、p2、p3都是 Promise 实例,如果不是,就会先调用下面讲到的 Promise.resolve 方法,将参数转为 Promise 实例,再进一步处理。另外, Promise.all() 方法的参数可以不是数组,但必须具有 Iterator 接口,且返回的每个成员都是 Promise 实例。
p的状态由p1、p2、p3决定,分成两种情况。
- 只有p1、p2、p3的状态都变成
fulfilled,p的状态才会变成fulfilled,此时p1、p2、p3的返回值组成一个数组,传递给p的回调函数。 - 只要p1、p2、p3之中有一个被
rejected,p的状态就变成rejected,此时第一个被reject的实例的返回值,会传递给p的回调函数。
Code
const requestsLimit = (list, limit, asyncHandle) => {
return new Promise(resolve => {
let _limit = limit;
let recordList = []; // 记录异步操作
let index = 0;
let listCopy = [].concat(list);
let asyncList = []; // 正在进行的所有并发异步操作
const asyncFunc = () => {
while(_limit--) {
const data = listCopy.shift()
if (data) asyncList.push(asyncHandle(data, index++));
}
Promise.all(asyncList).then(response => {
// 监听并记录每一次请求的结果
recordList = recordList.concat(response.filter(item => item));
if (listCopy.length !== 0) {
_limit = limit;
asyncList = [];
asyncFunc() // 数组还未迭代完,递归继续进行迭代
} else {
// 所有并发异步操作都完成后,本次并发控制迭代完成,返回记录结果
resolve(recordList)
}
})
}
asyncFunc()
})
}
Demo
var dataLists = [1,2,3,4,5,6,7,8];
requestsLimit(dataLists, 3, (item, index) => {
return new Promise(resolve => {
// 执行异步处理
setTimeout(() => {
// 筛选异步处理的结果
console.log(index)
if (item % 2 === 0) resolve({ item, index })
else resolve()
}, Math.random() * 5000)
});
}).then(response => {
console.log('finish', response)
})
console.log()
改进(2022.08.31)
这一帖子是之前学习Promise封装的函数,实现的思路考虑的不够全面。但是没想到还是有很多人看到了这个贴子,下面贴上改进的代码。
function requestsLimit(
list = [],
limit = 3,
asyncHandle = () => Promise.resolve()
) {
return new Promise((r, j) => {
const _request = list.slice()
const queue = [] // 任务队列
const result = [] // 结果数组
let index = 0
let abort = false // 中止开关
if (!_request.length || !limit) return r(result)
function requestHandler() {
const item = _request.shift()
if (!item) {
// 判断队列是否还有任务
if (queue.length) return
return r(result)
}
const handler = asyncHandle(item, index++)
// 回调函数返回 非 Promise 则中止
abort = !(handler instanceof Promise)
if (abort) {
if (queue.length) return
return r(result)
}
queue.push(
handler.then(res => {
result.push(res)
})
.catch(err => {
result.push(err)
})
.finally(() => {
queue.shift()
// 判断队列是否还有任务 或 是否中止
if (_request.length && !abort) {
requestHandler()
} else if (!queue.length) {
abort ? j(result) : r(result)
}
})
)
}
for (let i = 0; i < limit; i++) {
requestHandler()
}
})
}
传送门
- 阮一峰 --- ECMAScript 6 入门 - Promise
- 个人博客 --- 猫十一の纸盒子