ES7版
git地址:async-pool
使用
先用nodejs搞个接口
const http = require('http')
const Url = require('url')
const server = http.createServer((request, response) => {
const { url } = request
const parameter = Url.parse(url, true).query
const count = Number(parameter.count)
response.setHeader('Access-Control-Allow-Origin', '*')
response.setHeader('Content-type', 'application/json')
setTimeout(() => {
response.end(parameter.count)
}, count * 1000)
})
server.listen(3000, () => console.log('服务已开启!'))
客户端使用
偷懒用一下 axios (也没偷上, 找 CDN 还得找一会, 不如 XMLHttpRequest 写了)
<script src="https://cdn.jsdelivr.net/npm/axios/dist/axios.min.js"></script>
<script>
function api(count) {
return axios.get('http://192.168.5.8:3000/', {
params: { count }
})
}
asyncPool(2, [1, 1, 1, 1], api)
</script>
源码分析
/*
poolLimit: 可同时发起请求的最大数量
array: 将依次作为 iteratorFn 的参数执行
iteratorFn 一个函数 需要返回 Promise
*/
async function asyncPool(poolLimit, array, iteratorFn) {
// 最终传给 Promise.all
const ret = []
// 正在执行的任务 -就是还没有被解决的 Promise 数组, 这里的 Promise 处于 pending 状态 -限制最大数量就靠它了
const executing = []
// ES6 迭代 -数组可被 `for of` 迭代, 对象不可以
for (const item of array) {
// 每次迭代 都向结果 push 新的 Promise 至于为什么又套了一层 Promise.resolve(), 是为了防止使用的时候没有传入 Promise 导致后续代码执行错误
const p = Promise.resolve().then(() => iteratorFn(item, array))
ret.push(p)
// 限制并发的逻辑
if (poolLimit <= array.length) {
/*
在 then 的回调函数中, 当这个 Promise 被解决后, 由 pending 变为 fulfilled
已经拿到了服务端数据, 就删除该 Promise,腾出地方添加下一个 Promise,始终保持 executing 内有两个元素
then 的回调是异步, 别认为 `splice` 在 `executing.push(e)` 的上边很奇怪
// 感觉这样写也可以呢,为什么要又定义一个变量 e 呢
p.then(() => executing.splice(executing.indexOf(p), 1))
executing.push(p)
*/
const e = p.then(() => executing.splice(executing.indexOf(e), 1))
executing.push(e)
/*
当正在执行的任务到达限制数量的时候, 利用 await 等待执行
Promise.race 的作用: 假如 poolLimit 是 2, executing 的任务有任意一个被解决,
Promise.race 就是 fulfilled 状态, 之后进入第 3 次 for 循环
*/
if (executing.length >= poolLimit) {
await Promise.race(executing)
}
}
}
// 最后按顺序返回结果
return Promise.all(ret)
}
疑惑
-
疑惑一
const p = Promise.resolve().then(() => iteratorFn(item, array))没有理解源码中这句为什么要套一层
Promise.resolve()测试改成下面也可以呀
const p = iteratorFn(item, array) -
疑惑二
对于这两句,为什么又定义了一个变量 e 呢?
const e = p.then(() => executing.splice(executing.indexOf(e), 1)) executing.push(e)这块代码的目的就是在获取到结果后删除该 Promise 留出地方好添加下一个Promise呀
测试改成下面也可以呀
p.then(() => executing.splice(executing.indexOf(p), 1)) executing.push(p)