自定义 for..of 使用队列来解决 promise 中的并发问题会产生比 Promise.all 更慢的请求

Custom for..of using queue to solve concurrency in promises yields slower requests than Promise.all

这是我的问题: 我有一堆 x 承诺,我想一次解决 5 个(基本上是 Plimit 所做的)

这是我使用 for...of

实现的代码
async queuefy(items, concurrency, callback) {
    console.log(items.length);
    let queue = [];
    let singleQueue = [];
    let now = 0;
    items.forEach((item, index) => {
        now++;
        singleQueue.push(item);
        if(now === concurrency || index === items.length - 1) {
            queue.push(singleQueue);
            now = 1;
            singleQueue = [];
        }
    });
    let batch = 0;
    let ret = [];
    for(const que of queue) {
        const currentRes = await Promise.all(que.map(async (q) => {
            return await callback(q);
        }));
        console.log("Resolved batch: ", ++batch);
        ret.push(...currentRes);
    }
    ret = ret.filter(ret => ret !== undefined);
    return ret;
}

还有更复杂的逻辑来解决,使用好的 ol' 会增加时间 Promise.all。仅测试了最多 15 个“queuefy”实例。

我的代码有问题吗?或者对于更大的例子使用 Promise.all 请求时间会减少吗?

不足为奇的是,如果在同时请求 15 个项目和一次最多请求 5 个请求之间进行选择,throttled/queued 实施需要更长的时间。并行完成的工作现在正在完成 semi-serially。据推测,这里使用节流或串行行为并不是为了绝对节省 wall-clock 时间,而是为了平衡服务器负载或允许 browser/network 在其自身限制内优先考虑其他连接。

您的代码的一个方面可能出乎您的意料,但是:您的 Promise.all 批 5 现在需要的时间与下一批开始之前的最长请求一样长。因此,就在新批次开始之前,当前打开的请求可能远少于 5 个。

(A1-------)             (B1-----------)(C1--)                 |
(A2---------)           (B2--)         (C2-------------------)|
(A3---)                 (B3-------)    (C3------)             | done
(A4--------------------)(B4----)       (C4----------)         |
(A5-----)               (B5---------)  (C5----)               |

TIME->--------------------------------------------------------|

PLimit and 等其他实现中,没有对 Promise.all 的中间调用,因此您可以更优化地完成这些调用,同时确保同时存在不超过 5 个开放承诺。

( 1-------)( 8-----------)(14--)            |
( 2---------)( 9--)(11-------------------)  |
( 3---)( 6-------)(10------)                | done
( 4--------------------)(13----)            |
( 5-----)( 7---------)(12----)(15----------)|

TIME->--------------------------------------|

出于这个原因,您可能希望放弃使用 Promise.all 以更好地保持开放 channels/threads 饱和。