使用 Promise.all 时 Promise 之间的延迟

问题描述 投票:0回答:7

有没有办法使用 Promise.all() 延迟对 Promise 数组的评估?

在将它们添加到数组之前,手动将延迟函数添加到每个 Promise 的末尾是否有意义?

Promise.all([p1,p2,p3]).then(res => console.log(res))

我想添加延迟,因为我的服务器无法一次处理太多请求。

ecmascript-6 promise
7个回答
27
投票

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(...);

相同的解决方案可用于在循环内批量创建请求承诺。

延迟承诺的秘诀可以在这个答案中找到。


7
投票

是的,你可以使用 Promise.all 延迟 Promise 来创建交错执行,而且这很容易做到:

// 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);
});

您也可以在这里运行它。

与其他答案中所示的可以使用 .then() 完成的链之间的延迟不同,这是每个 Promise 都不同的延迟,因此当您调用 Promise.all() 时,它们将交错排列。例如,当您调用具有速率限制的 API 时,如果并行触发所有调用就会违反该速率限制,这会非常有用。

和平


6
投票

我需要动态创建调用,因此根据 @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);

3
投票

对我来说最简单的解决方案似乎是只获取生成承诺的映射函数的当前索引,并使用该索引作为延迟的基础:

const sleep = ms => new Promise(resolve => setTimeout(resolve, ms))

await Promise.all(
  dataPoints.map(async (dataPoint, index) => {
    await sleep(index * 1000)
    ...

这使得每个操作等待索引 * 1 秒才能被触发,从而有效地在每个操作之间放置 1 秒的延迟。


0
投票

有没有办法延迟使用以下方法来延迟对一系列承诺的评估

Promise.all()

不。承诺不会被“评估”,它们只是解决。何时发生这种情况是由它们的创造者决定的,而不是其他的。当

Promise.all
被调用时,promise
p1
p2
p3
已经被创建(并且它们的异步任务可能已经开始)。


0
投票

实现此目的的另一种方法是劫持循环的转译方式:

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函子的异步上下文中。如果你使用map,它会同时执行所有内容,最后延迟1秒。


0
投票

这是我使用 async/await 的解决方案。

const requestsWithDelay = async () => {
  let delay = 0;
  const delayIncrement = 1000;
  const items = [...];
  const promises = items.map((n) => {
    delay += delayIncrement;
    return new Promise(async (resolve, reject) => {
      try {
        await setTimeout(async () => {
          await asyncRequest(); // <-- async request
          resolve();
        }, delay);
      } catch (error) {
        reject(error);
      }
    });
  });

  await Promise.all(promises);
};
© www.soinside.com 2019 - 2024. All rights reserved.