使用 Promise.all 时承诺之间的延迟
Posted
技术标签:
【中文标题】使用 Promise.all 时承诺之间的延迟【英文标题】:Delay between promises when using Promise.all 【发布时间】:2018-05-05 07:34:23 【问题描述】:有没有办法使用 Promise.all() 延迟对一组承诺的评估?
在将它们添加到数组之前手动将延迟函数添加到每个 Promise 的末尾是否有意义?
Promise.all([p1,p2,p3]).then(res => console.log(res))
我想添加一个延迟,因为我的服务器无法一次处理太多请求。
【问题讨论】:
我认为这不会在代码方面进行扩展,假设我想调用 100 个承诺。这将导致一个非常长的文件。这就是为什么我想尝试使用 Promise.all() 以编程方式完成它 promise 在调用时创建。 Promise.all 只是在所有调用完成后通知您。它不会耽误他们。您实际上需要延迟拨打电话,而不是承诺。 这很有意义,谢谢@OriDrori。 根据您实际创建所有这些承诺的方式,您可能对 npmjs.com/package/p-limit 感兴趣 如果要序列化调用,请查看 async await。 【参考方案1】:对我来说,最简单的解决方案似乎是只获取产生承诺的 map 函数的当前索引,并使用该索引来延迟:
const sleep = ms => new Promise(resolve => setTimeout(resolve, ms))
await Promise.all(
dataPoints.map(async (dataPoint, index) =>
await sleep(index * 1000)
...
这使得每个操作等待索引 * 1 秒被触发,有效地在每个操作之间放置 1 秒的延迟。
【讨论】:
【参考方案2】:另一种方法是劫持循环的转译方式:
async function doABatchOfAsyncWork(workItems)
for (const item of workItems)
await workTask(item)
await delay(1000) // not built-in but easily implemented with setTimeout + promise
当然,您也可以保存这些值并在最后返回它们,这与您通常在 for 循环中所做的完全一样。您不能使用 map 执行此操作,因为 await 必须位于传入的 map-functor 的异步上下文中。如果您使用 map,它将同时执行所有操作,最后延迟 1 秒。
【讨论】:
【参考方案3】:我需要动态创建调用,因此根据@estus-flask 的回答,设法提出:
let delay = 0; const delayIncrement = 1000;
const promises = items.map(item =>
delay += delayIncrement;
return new Promise(resolve => setTimeout(resolve, delay)).then(() =>
fetch(...);
)
let results = await Promise.all(promises);
【讨论】:
【参考方案4】:是的,您可以使用 Promise.all 延迟承诺以创建交错执行,这很容易做到:
// Promise.all() with delays for each promise
let tasks = [];
for (let i = 0; i < 10; i++)
const delay = 500 * i;
tasks.push(new Promise(async function(resolve)
// the timer/delay
await new Promise(res => setTimeout(res, delay));
// the promise you want delayed
// (for example):
// let result = await axios.get(...);
let result = await new Promise(r =>
console.log("I'm the delayed promise...maybe an API call!");
r(delay); //result is delay ms for demo purposes
);
//resolve outer/original promise with result
resolve(result);
));
let results = Promise.all(tasks).then(results =>
console.log('results: ' + results);
);
您也可以运行它here。
而不是链之间的延迟,可以使用 .then() 来完成,如其他答案所示,这是每个 Promise 不同的延迟,因此当您调用 Promise.all() 时,它们将交错。例如,当您调用具有速率限制的 API 时,这很有用,您会通过并行触发所有调用来违反该速率限制。
和平
【讨论】:
这与@estus-flask 的答案相同,但在循环中 有点不同,因为它更真实。您通常必须根据您提前不知道的事情进行循环。此外,它使用 await 来阻止而不是将逻辑放在并不总是有效的 setTimeout 中,特别是如果您还必须在那里阻止。【参考方案5】:Promise.all
旨在解决承诺何时履行,但无论Promise.all
如何,都会评估现有承诺。
为了做到这一点,最初应该创建 Promise 以产生延迟:
const delayIncrement = 500;
let delay = 0;
const p1 = new Promise(resolve => setTimeout(resolve, delay)).then(() => fetch(...));
delay += delayIncrement;
const p2 = new Promise(resolve => setTimeout(resolve, delay)).then(() => fetch(...));
delay += delayIncrement;
...
Promise.all([p1,p2,p3]).then(...);
同样的解决方案可用于在循环内批量创建请求承诺。
延迟承诺的方法可以在this answer找到。
【讨论】:
完美解决方案!【参考方案6】:有没有办法延迟对一系列承诺的评估使用
Promise.all()
?
没有。承诺没有“评估”,它们只是解决。什么时候发生这种情况是由他们的创造者决定的,没有别的。当Promise.all
被调用时,p1
、p2
和p3
的promise已经被创建(并且它们的异步任务可能已经被启动了)。
【讨论】:
以上是关于使用 Promise.all 时承诺之间的延迟的主要内容,如果未能解决你的问题,请参考以下文章
使用 Array.map 时嵌套承诺,使用 Promise.all 但仍然无法正常工作
使用 Promise.all() 在 Promise 实现时执行操作
是否有可能在 Promise.all 中捕获所有被拒绝的承诺? [复制]