自定义 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 饱和。
这是我的问题: 我有一堆 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 饱和。