异步:手撕JavaScript 控制并发请求

1,034 阅读2分钟

目的

现有多个异步请求,需要并发请求,并限制并发数。

测试方法

let request = (delay, id) => {
    return new Promise((resolve) => {
        setTimeout(resolve, delay, id)
    })
}
let test_requests = [
    request(6000, 1),
    request(3000, 2),
    request(4000, 3),
    request(6000, 4),
    request(1000, 5),
    request(2000, 6),
]

Promise解法 (递归)

思路:

  1. 每层递归都往 run 里加 请求
  2. 递归过程中,如果 run 达到临界,使用 Promise.race 来触发 递归。
  3. 请求的 回调为从 run 中删除 自身
  4. 递归终点是 所有的 request 都已经加完,返回 resolve。此时,就像盗梦空间一样,这个resolve 会不断的被返回。(此刻,请求并没有结束)。最外层会接受这个resolve,使用Promise.all 等待所有请求结束,执行回调函数。
function limitPromise(requesets = [], max = 1, callback = () => { }) {
    let run = []
    let next = 0
    let doit = () => {
        if (next === requesets.length) {  // 递归终止条件 
            return Promise.resolve()
        }
        let thisRequest = requesets[next++]  // 获取请求 
        run.push(thisRequest.then(requestRes => { // 请求推入到run数组中
            console.log(requestRes) 
            run.splice(run.indexOf(thisRequest), 1) // 删掉自己
        }))
        let res = null
        if (run.length === max) {
            res = Promise.race(run) // 需要等待一个请求到达
        }else{
            res = Promise.resolve() // 不需要等待
        }
        return res.then(()=>doit()) // 向下递归
    }
    doit().then(() => { 
    	// 此时 run 里的请求还没结束 
        Promise.all(run).then(() => callback())
    })
}
limitPromise(test_requests, 3, () => console.log("requeset end"))

升级版:使用async、await

递归算法都有迭代写法。实际上这个题目,使用迭代更好理解。 这里使用await语法来实现 run 满的等待。 await promise 会暂停函数执行,直到 promise 成功。 这里的逻辑非常简单,就是便利请求数组,把它们加到run 中。但是,由于max的存在,需要在 run.length === max 时,使用await等待,race 到达。同样,添加完后,需要等待 run 里所有请求结束,然后执行回调函数。

async function limitAsync(requesets = [], max = 1, callback = () => { }) {
    let run = [], i = 0;
    for (const request of requesets) { // 数组遍历
        run.push(request.then((requesetRes) => { // 依次 推入到 run
            console.log(requesetRes)
            run.splice(run.indexOf(request), 1)
        }))
        if (run.length === max) { 
            console.log('wait')
            await Promise.race(run) // 等待 race 到达。
        }
    }
    Promise.all(run).then(() => callback())
}
limitAsync(test_requests, 3, () => console.log("requeset end"))

创作不易!如果对你有帮助,还请点赞收藏。 如果有疑惑可以在下方留言。 如果有什么建议,可以私信我。