所谓并发请求,就是指在一个时间点多个请求同时执行。当并发的请求超过一定数量时,会造成网络堵塞,服务器压力大崩溃或者其他高并发问题,此时需要限制并发请求的数量。

假如等待请求接口1000个,限制每次只能发出100个。即同一时刻最多有100个正在发送的请求。每当100个之中有一个请求完成时,则从待请求的接口(即剩余的900个待请求接口)中再取出一个发出。保证当前并发度仍旧为100。直至全部接口请求完成。

// api接口请求列表
	const apiList = [
	    'url___A',
	    'url___B',
	    'url___C',
	    'url___D',
	    'url___E',
	    'url___F',
	    'url___G',
	]
	
	// 记录当前正在请求的接口,调试代码用
	let currentRequestList = []
     
        
	// 模拟请求数据
    const request = api => {
        console.log(`${api} 请求start`)
        // 请求时间 0 ~ 3 秒
        const wait = Math.random() * 3000
        console.log(`${api} 请求时间 ${wait}`)
        // currentRequestList 收集正在请求的接口
        currentRequestList.push(api)
        // 满足限制条件时,控制台显示当前正在请求的接口
        if (currentRequestList.length === 2) {
            console.log('当前正在请求的接口 -> ', currentRequestList.toString())
        }
        return new Promise(resolve => {
            setTimeout(() => {
                console.log(`${api} 请求end`)
                // 请求完成时,currentRequestList 删除该请求
                const index = currentRequestList.findIndex(c => c === api)
                currentRequestList.splice(index, 1)
                resolve(`获取接口“${api}”的数据`)
            }, wait)
        })
    }
/**
	 * @descriptio 并发请求且限制请求数量
	 * @param {apiList} 请求接口列表
	 * @param {limit} 限制请求接口的数量,默认每次最多发送3次请求
	 * @param {callback} 回调函数
	 */		
    const requestWithLimit = (apiList, limit = 3, callback) => {

        // 请求数量记录,默认为 0
        let count = 0

        // 递归调用,请求接口数据
        const run = () => {
            // 接口每调用一次,记录数加 1
            count++
            const api = apiList.shift()
            request(api).then(res => {
                // 接口调用完成,记录数减 1
                count--
                console.log(res)
                // apiList 长度不为 0 且记录小于限制的数量时递归调用
                if (apiList.length && count < limit) {
                    run()
                }
                // apiList 为空且记录数减为初始值 0 时调用回调函数
                if (!apiList.length && !count) {
                	// 这里可以对所有接口返回的数据做处理,以便输出
                    callback('全部执行完毕!')
                }
            })
        }

        // 根据 limit 并发调用
        for (let i = 0; i < limit; i++) {
            run()
        }
    }
	
	requestWithLimit(apiList, 2, res => {
		console.log('回调函数', res)
	})

storeAccessToken 并发请求 请求并发限制_回调函数

用人说用 for 循环来并发太 low 了,能否采用 Promise.all 的写法呢? 这个想法很不错,答案是可以的。Promise.all 接收一个元素为 Promise 对象的数组作为参数,这里控制并发的本质就是控制该数组的长度。

Promise.all([p1, p2, p3...])  		// p1, p2, p3...为 Promise 对象
/**
	 * @description 限制并发请求
	 * @param {apiList} 请求接口列表
	 * @param {limit} 限制请求接口的数量,默认每次最多发送3次请求
	 * @return { Promise<Array> } resList
	 */
	 // 这里没有 callback 是因为返回的是 Promise 对象,其 then 方法取代了 callback
    const requestWithLimit = (apiList, limit = 3) => {
    	// apiList 的副本,避免 shift 方法对参数造成影响
        let list = [...apiList]
        // 用来记录api - response 的映射
  		// 保证输出与输入顺序一致
        let map = new Map()
        // 递归调用
        const run = () => {
            if (list.length) {
                const api = list.shift()
                return request(api).then(res => {
                    map.set(api, res)
                    return run()
                })
            }
        }
        // 当 apiList.length < limit 的时候,避免创建多余的 Promise
        const promiseList = Array(Math.min(apiList.length, limit)).fill(Promise.resolve()).map(promise => promise.then(run))
        return Promise.all(promiseList).then(() => {
            return apiList.map(c => map.get(c))
        })
    }
    
	requestWithLimit(apiList, 2).then(res => {
		console.log('请求完毕:', res)
	})

storeAccessToken 并发请求 请求并发限制_回调函数_02