简化循环函数并向 Promise.all() 请求添加定时速率限制

Simplify Looped Function And Adding Timed Rate Limit To Promise.all() Request

提问人:z123 提问时间:10/23/2023 更新时间:10/23/2023 访问量:47

问:

我有以下函数,可以循环到图像并将关联的图像添加到项目中。虽然它运行良好,但我正在尝试配置两件事,但无法正常工作。

第一个问题是我有承诺。All(promises),我想按持续时间为其添加速率限制。因此,无论是否有 10、20、50、100 个承诺,我都希望将限制设置为每分钟 60 个请求,但希望所有承诺都能解决。这是否可能,或者我是否必须修改我的代码以使用另一个库或可能的时间间隔。

第二个问题是,如果有一种方法可以简化这个功能,那也会有很大帮助,因为它似乎有太多的循环,可能会减慢 api 的速度。对代码的任何改进将不胜感激

function findImagesForObjectsOrGenerate(listOfObjects, images, isCategory, merchantId) {
  let promises = [];
  if (listOfObjects.length > 0) {
    listOfObjects.forEach((object) => {
      const matchedImage = images.find(image => {
        return object.id === image.cloverDataId;
      });

      if (matchedImage) {
        object.imageData = matchedImage.imageData;
      } else {
        promises.push(imageService.generateImageByNameAndSave(object.name, isCategory, object.id, merchantId));
      }
    })
  }
  return Promise.all(promises).then(values => {
    values.forEach((response, i) => {
      if (response) {
        listOfObjects[i].imageData = response.imageData;
      }
    })
    return listOfObjects;
  });
}
JavaScript 节点 .js 表示 承诺 速率限制

评论


答:

1赞 Ale_Bianco 10/23/2023 #1

您可以实现一个简单的速率限制器,用于控制请求的速率。下面是一个示例,说明如何修改函数以将请求限制为每分钟 60 个:setTimeout

function findImagesForObjectsOrGenerate(listOfObjects, images, isCategory, merchantId) {
  const requestsPerMinute = 60;
  const delay = 60 * 1000 / requestsPerMinute; // Delay between each request (in milliseconds)

  const processObject = async (object) => {
    const matchedImage = images.find(image => object.id === image.cloverDataId);

    if (matchedImage) {
      object.imageData = matchedImage.imageData;
    } else {
      const response = await imageService.generateImageByNameAndSave(object.name, isCategory, object.id, merchantId);
      if (response) {
        object.imageData = response.imageData;
      }
    }
  };

  const promises = listOfObjects.map(async (object) => {
    await processObject(object);
    return object;
  });

  return Promise.all(promises);
}

您可以考虑以下优化方法:

  • 如果请求非常大,您可能需要考虑对请求进行批处理,以避免服务器不堪重负。listOfObjects
  • 确保正确处理错误,尤其是与网络相关的错误。imageService.generateImageByNameAndSave

批处理示例:

function findImagesForObjectsOrGenerate(listOfObjects, images, isCategory, merchantId) {
  const MAX_REQUESTS_PER_MINUTE = 60;
  const requestsPerBatch = Math.min(MAX_REQUESTS_PER_MINUTE, listOfObjects.length);

  let promises = [];
  if (listOfObjects.length > 0) {
    listOfObjects.forEach((object) => {
      const matchedImage = images.find(image => {
        return object.id === image.cloverDataId;
      });

      if (matchedImage) {
        object.imageData = matchedImage.imageData;
      } else {
        promises.push(() => imageService.generateImageByNameAndSave(object.name, isCategory, object.id, merchantId));
      }
    })
  }

  // Define a function to execute a batch of promises
  function executeBatch(batch) {
    return Promise.all(batch.map(fn => fn()));
  }

  // Split promises into batches
  const batches = [];
  while (promises.length > 0) {
    batches.push(promises.splice(0, requestsPerBatch));
  }

  // Execute each batch with a delay between them
  let index = 0;
  function executeNextBatch() {
    if (index < batches.length) {
      return executeBatch(batches[index++])
        .then(() => new Promise(resolve => setTimeout(resolve, 60000 / MAX_REQUESTS_PER_MINUTE)))
        .then(executeNextBatch);
    }
  }

  return executeNextBatch().then(() => {
    return listOfObjects;
  });
}

评论

0赞 z123 10/23/2023
感谢您的回复。我会测试一下,让你知道它是怎么回事。您能否详细说明一下批处理请求,以及它们在防止系统不堪重负方面的作用。您能否提供一个示例,说明如何修改代码以支持批处理,以便更好地理解。再次感谢您的帮助!
0赞 Ale_Bianco 10/23/2023
我添加了一个带有批处理的示例
0赞 z123 10/23/2023
嘿,虽然您的批处理请求代码有效,但我仍然收到错误:429 反复超出速率限制。请在一小时后重试。使用 Open API 时,即使我的请求限制是每分钟 50 个。
0赞 z123 10/24/2023
感谢您的帮助。通过对您的代码进行一些调整,我设法使其正常工作,但我遇到了另一个问题,即 executeNextBatch() 并不总是返回 promise。你能看一下这个链接,看看你是否可以帮助解决这个问题。再次感谢!stackoverflow.com/questions/77347692/......