Promise控制并发

581 阅读2分钟

使用promise控制单个终端的并发场景

我们都知道浏览器中

同一域名下,
①同一GET请求的并发数是1,也就是说上一个请求结束,才会执行下一个请求,否则置入队列等待发送;
②不同GET/POST请求的并发数量是6,当发送的请求数量达到6个,并且都没有得到响应时,后面的请求会置入队列等待发送。

如果你的promises数组中每个对象都是http请求,或者说每个对象包含了复杂的调用处理。而这样的对象有几十个。

那么会出现的情况是,你在瞬间发出几十http请求(tcp连接数不足可能造成等待),或者堆积了无数调用栈导致内存溢出。

这时候,我们就需要考虑对Promise.all做并发限制。

Promise.all并发限制思路是,每个时刻并发执行的promise数量是固定的,我们使用Promise.race控制当前有执行完的任务立马启动下一个任务,最终的执行结果的顺序用Promise.all保持与输入的一致。

// 参考async-pool源码
function asyncPool(poolLimit, array, iteratorFn) {
    let i=0;
    // 缓存所有执行完成的promisepromise对象、最后需要放到promise.all中保证输出的顺序
    const ret = [];
    // 整在执行中的promise对象,不超过最大的限制
    const executing = new Set();

    const enqueue = () => {
        // 边界处理 空数组或者已经打到最大数量
        if (i === array.length) {
            return Promise.resolve();
        }
        // 每调一次enqueue,初始化一个promise
        const item = array[i++];
        const p = Promise.resolve().then(() => iteratorFn(item, array))
        ret.push(p)
        executing.add(p);
        // 定义一个执行完成之后在executing删除,实时更新executing的数量
        const clean = () => executing.delete(p);
        p.then(clean).catch(clean);
        // 使用Promise.rece,每当executing数组中promise数量低于poolLimit,就实例化新的promise并执行
        let r = Promise.resolve();
        if (executing.size >= poolLimit) {
            r = Promise.race(executing)
        }
        // 递归,直到遍历完array
        return r.then(() => enqueue())
    }

    return enqueue().then(() => Promise.all(ret));
}

// 使用
asyncPool(10, [/* 40 个请求的参数数据 */], () => {/* 发起请求的函数 */})