使用 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被调用时,p1p2p3的promise已经被创建(并且它们的异步任务可能已经被启动了)。

【讨论】:

以上是关于使用 Promise.all 时承诺之间的延迟的主要内容,如果未能解决你的问题,请参考以下文章

使用 Array.map 时嵌套承诺,使用 Promise.all 但仍然无法正常工作

使用 Promise.all() 在 Promise 实现时执行操作

并行解决承诺

是否有可能在 Promise.all 中捕获所有被拒绝的承诺? [复制]

JavaScript 中的 Promise.all:如何获得所有承诺的解析值?

循环 await Promise.all 内存泄漏