【问题标题】:How do you run a setTimeout once a second inside a forEach loop?如何在 forEach 循环中每秒运行一次 setTimeout?
【发布时间】:2019-09-04 05:43:18
【问题描述】:

我的代码是为了在每分钟有 60 次写入限制的在线服务中创建多个展示位置:

  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 个展示位置列表的等待时间,等待时间可能会过长。

有没有更好的方法来重构此代码,以便无论如何每秒构建一个请求?如果没有那个“* (index + 1)”,它似乎会在 60 岁之后继续尝试一次构建所有内容。

我尝试使用 promises 和 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 测试中进行所有调用。

另外,值得注意的是,我在请求调用的回调中有一个解析。它解决了,所以即使我的应用程序的下一部分一直到最后。这就是为什么我在这里没有拒绝或解决。

【问题讨论】:

  • 这是在 Node.js 中吗?
  • 你能告诉我们你是如何尝试使用承诺的吗?注意you can't use forEach with them.
  • 是的,它是节点,我已经编辑了我的问题,以展示我最后一次使用 promises/async/await 的尝试。欣赏它

标签: javascript loops foreach async-await settimeout


【解决方案1】:

最好的选择是有一个返回 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 限制,但如果站点位于代理服务器后面,则在很短的时间内可能会有硬编码的请求限制,这也可能触发.
  • 作者说每分钟写入限制60次
  • 是的,正如我在评论中所说的那样。这是 API 强制 限制。许多服务器也支持某种 DDoS 保护,这些保护通常有自己的限制,不一定在 API 文档中披露。
【解决方案2】:

如何在 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,但它需要处理错误。可以进行更多重构。

我认为,这里最显着的改进是我们没有提前推送请求。这样我们就可以保持控制,并且如果需要更改或出现任何问题,我们可以跳出循环而不会再向服务器发送垃圾邮件。

【讨论】:

  • 效果很好,谢谢!它似乎是最容易实现的,并且适合我在上面的示例中已经完成的操作。我能够让它工作,现在我自信地知道,即使要构建 3000 个展示位置,我也不会等待越来越长的时间来完成操作。
猜你喜欢
  • 1970-01-01
  • 2023-03-11
  • 2015-02-05
  • 1970-01-01
  • 1970-01-01
  • 2013-11-28
  • 2020-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多