【问题标题】:Delay between promises when using Promise.all使用 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。

标签: ecmascript-6 promise


【解决方案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已经被创建(并且它们的异步任务可能已经被启动了)。

        【讨论】:

          猜你喜欢
          • 2017-04-26
          • 2018-05-03
          • 1970-01-01
          • 2015-03-08
          • 1970-01-01
          • 1970-01-01
          • 2015-03-27
          • 2023-03-21
          相关资源
          最近更新 更多