如何在forEach循环内每秒运行一次setTimeout?

3

我写的代码旨在创建多个放置位于一个每分钟只能写入60次的在线服务中:

Original Answer翻译成"最初的回答"

  placementsToAdd.forEach((placement, index) => {
    setTimeout(() => {
      options.url = `https://api.company.com/placement?publisher_id=${existingPub ? existingPub : placementsJson[0].PublisherId}&site_id=${placement.siteId}`
      options.body = `{"placement":{"name":"${placement.placement}"}}`
      request(options, callback);  
    },1000 * (index + 1))
  })

它是这样工作的,但如果一次有2000或3000个放置列表,等待时间可能会过长。是否有更好的方法重构此代码以便每秒建立一个请求?没有 "*(索引+1)",它似乎仍在尝试一次构建所有内容,在60后撞到墙上。我尝试过使用promise和async/await(这对我来说是新的),但似乎没有改变行为。谢谢!按要求展示了如何在此代码中使用promise:
  async function createThePlacements() {
    let promise = new Promise((resolve, reject) => {
      for (let i = 0; i < placementsToAdd.length; i++) {
        setTimeout(() => {
          options.url = `https://api.company.com/placement?publisher_id=${existingPub ? existingPub : placementsJson[0].PublisherId}&site_id=${placementsToAdd[i].siteId}`
          options.body = `{"placement":{"name":"${placementsToAdd[i].placement}"}}`
          request(options, callback);  
        },1000)
      }
    });

    let result = await promise; // pause till the promise resolves 
    console.log('result - ', result);
  }

  createThePlacements();

所以,先声明一下 - 正如我之前提到过的那样,我从未使用过Async Await,所以我正在阅读相关资料尝试理解它的工作原理。这似乎是语法,但我的结果目前似乎什么都没有发生,但代码也继续执行它该做的事情,只是尝试让我的300个测试调用同时进行。

另外,值得注意的是,我在请求调用的回调函数内有一个resolve。它会被解决,因此即使我的应用程序的下一部分全部完成,它也会一直运行到最后。这就是为什么我这里没有放置拒绝或解决方案。

Original Answer翻译成:"最初的回答"


这是用Node.js写的吗? - Ry-
1
请问你能展示一下你如何尝试使用Promise吗?请注意,你不能在它们上使用forEach - Bergi
是的,这是Node,并且我已经编辑了我的问题,展示了我最后一次尝试使用promises / async / await。非常感谢。 - nyhunter77
2个回答

4
你如何在forEach循环内每秒运行一次setTimeout?
最简单的方法是:
const wait = ms => new Promise(resolve => setTimeout(resolve, ms));

for (const placement of placementsToAdd) {
  const options = {...};
  request(options, callback);  
  await wait(1000);
}

await 在普通的 for 循环中的表现是可预测的,但在 forEach 中则不是。

我没有改动你的 callback 代码,但它需要处理错误。还有更多的重构可以做。

我认为这里最显著的改进是我们不会提前推送请求。这样我们保留了控制权,如果需要进行更改或出现问题,我们可以在不连续向服务器发送请求的情况下跳出循环。


2
这个方法真的很好用,谢谢!它似乎是最容易实现的,并且与我之前示例中已经完成的内容非常契合。我成功地让它运行起来了,现在我可以自信地知道,即使有3000个放置需要构建,我也不会越来越长时间地等待操作完成。 - nyhunter77

0
最好的选择是拥有一个返回 Promise 的 request 方法。
然后你可以像这样重写你的代码。
function sleep(ms) {
  return new Promise(resolve => setTimeout(resolve, ms));
}

function requestPlacement(placement) {
  const options = {...};
  return request(options);
}

async function requestAllPlacements(placements) {
  for(let i = 0; i < placements.length; i+=60) {
    if (i > 0) {
      // wait 1 minute
      await(sleep(60000));
    }

    await Promise.all(
      placements
        .slice(i, 60)
        .map(requestPlacement);
    );
  }
}

每分钟可能有60个API请求的限制,但如果网站在代理服务器后面,还可能存在一个在短时间内的请求数量的硬编码限制,这也可能会触发。 - Patrick Roberts
作者说每分钟只能写入60条数据 - Olivier Boissé
是的,正如我在评论中所说的那样,这是一个由API强制执行的限制。许多服务器也会采取某种形式的DDoS保护措施,通常会有自己的限制,这些限制可能不一定在API文档中公开披露。 - Patrick Roberts

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接