基于Promise.all实现多个并发请求的瞬时控制

5,502 阅读3分钟

SPA下的ajax请求

由于现在大多数网站都是单页面应用,导致加载一个页面可能会出现几十个甚至上百个网络请求的情况出现,但是浏览器对于请求并发数是有限制的,一般为4~7个请求,并且高并发请求会导致页面卡死的情况出现,所以有必要基于原生js来使用Promise开发一个可以在同一时刻控制并发请求个数,使其可以多个并发请求资源但又不至于导致页面卡死。

基于原生js实现

先用代码模拟多个并发请求

const delay = function delay(interval) {
    return new Promise((resolve, reject) => {
        setTimeout(() => {
            resolve(interval)
        }, interval);
    })
}

let tasks = [
    () => { return delay(1000) },
    () => { return delay(1003) },
    () => { return delay(1005) },
    () => { return delay(1002) },
    () => { return delay(1004) },
    () => { return delay(1006) }]

这里使用setTimeout异步函数来模拟来模拟多个并发请求的情况,现将多个并发放置在数组 tasks 里面,如果并发量不高的话,可以直接使用Promise.all 来实现多个并发请求的情况,但是在高并发的情况下,没有解决大量并发带来的页面卡顿的问题,另外基于Promise.all,会出现一个异步任务失败,返回的是失败信息。所以我们基于原生js写出一个函数来实现同一时刻实现制并发数量。

代码实现如下:

function createRequest(tasks, pool) {
    pool = pool || 5;
    let results = [];
    let together = new Array(pool).fill(null);
    let index = 0;
    together = together.map((item, i) => {
        return new Promise((resolve, reject) => {
            const run = function run() {
                if (index >= tasks.length) {
                    resolve();
                    return;
                }
                let old_index = index;
                // 从任务池拿任务,由于index是升级作用域的变量,所以多个Promise共享一个index
                //这样可以让一个数组里面的任务一次执行
                let task = tasks[index++];
                task().then((result) => {
                	// 将返回的结果放置在results里面,实现请求数据的集中存储。
                    results[old_index] = result;
                    // 只有在上一个任务执行成功后才会执行一个异步任务
                    run();
                }).catch((reason) => {
                    reject(reason)
                })
            }
            run();
        })
    })
    // 多个promise同时处理,根据pool来限制同一时刻并发请求的个数
    return Promise.all(together).then(() => results)
}

createRequest根据你传入的pool将一组并发请求分割在不同Promise组成的区域里面,并且由于是递归的实现方法,在一个区域里面,只有上一个任务执行成功,才会执行下一个。同一时刻并发请求的个数为pool,这样就可以解决瞬时高并发带来的页面卡顿问题。并且将异步任务的执行结果放置到一个数组里面集中存储,不论成功或者失败,都返回这个数组,这样就可以在失败的情况下也能使用异步任务执行成功返回的数据。

执行代码:

createRequest(tasks, 3).then((results) => {
    console.log('success->', results);
}).catch((reason) => {
    console.log('fail->', reason);
})

返回的结果:

根据map函数的回调函数的index值可以看出同时只有3个异步任务在执行。至此就实现了异步任务的并发请求控制。