【问题标题】:async/await with Limiter for sending requests使用限制器异步/等待发送请求
【发布时间】:2020-09-22 09:15:32
【问题描述】:

我正在尝试限制发送到 API 的请求数量。

我正在使用 Limiter,它的工作方式与我的需要一样,唯一的问题是我找不到将它与 await 一起使用的方法(在呈现我的页面之前我需要所有响应)

有人可以帮帮我吗?

顺便说一句,日志返回一个布尔值。

const RateLimiter = require('limiter').RateLimiter;

const limiter = new RateLimiter(50, 5000)

for (let i = 0; i < arrayOfOrders.length; i++) {
    const response = limiter.removeTokens(1, async (err, remainingRequests) => {
        console.log('request')
        return await CoreServices.load('updateOrder', {
            "OrderNumber": arrayOfOrders[i],
            "WorkFlowID": status
        })
    })
    console.log('response', response)
}

console.log('needs to log after all the request');

这是登录:

response true
response true
response false
needs to log after all the request
request
request
request
...

【问题讨论】:

  • 在您的情况下,.map() 将返回一组承诺。如果你想等待所有这些都完成,那么你需要做await Promise.all(arrayOfOrders.map(...))。或者,如果您可以对它们进行排序,请将.map() 更改为常规的for 循环,然后循环内的await 实际上会做一些有用的事情。请记住,.map() 不是承诺或await 知道所以return await Coreservices.load(...) 不会等待任何东西。它只是向.map()返回一个承诺。
  • 这不是问题,使用它也不起作用。它仍然没有等待所有请求完成才能继续。我相信这是限制器。
  • 用@jfriend00 的想法更新了代码
  • 我不知道limiter.removeTokens() 应该做什么,但如果你混合使用承诺和常规回调,你注定要控制流程。你无法控制它。我猜你可以承诺limiter.removeTokens() 然后await 这个结果。
  • 检查答案 - 我认为它会满足您的需求

标签: javascript node.js async-await rate-limiting


【解决方案1】:

承诺.removeTokens 会有所帮助,看看这段代码是否有效

const RateLimiter = require('limiter').RateLimiter;

const limiter = new RateLimiter(50, 5000);

const tokenPromise = n => new Promise((resolve, reject) => {
    limiter.removeTokens(n, (err, remainingRequests) => {
        if (err) {
            reject(err);
        } else {
            resolve(remainingRequests);
        }
    });
});
(async() => { // this line required only if this code is top level, otherwise use in an `async function`
    const results = await Promise.all(arrayOfOrders.map(async (order) => {
        await tokenPromise(1);
        console.log('request');
        return CoreServices.load('updateOrder', {
            "OrderNumber": order,
            "WorkFlowID": status
        });
    }));
    console.log('needs to log after all the request');
})(); // this line required only if this code is top level, otherwise use in an `async function`

解释

首先:

const tokenPromise = n => new Promise((resolve, reject) => {
    limiter.removeTokens(n, (err, remainingRequests) => {
        if (err) {
            reject(err);
        } else {
            resolve(remainingRequests);
        }
    });
});

承诺 limiter.removeTokens 在 async/await 中使用 - 在 nodejs 中,您可以使用内置的 promisifier,但是最近我遇到了太多失败的实例 - 所以手动承诺(我编了很多这里的单词!)同样有效

现在代码很简单 - 您可以使用 arrayOfOrders.map 而不是 for 循环来创建一个承诺数组,这些承诺在速率限制允许的范围内并行运行,(速率限制在回调内部完成)

await Promise.all(... 将等到所有 CoreServices.load 都完成(或者一个失败 - 如果需要,您可以使用 await Promise.allSettled(... 代替)

地图回调中的代码标记为async 所以:

await tokenPromise(1);

将等到removeTokens 回调被调用 - 然后是请求

return CoreServices.load

制作

注意,这最初是 return await CoreServices.load,但 await 是多余的,因为异步函数中的 return await somepromise 与 return somepromise 相同 - 所以,也要调整你的代码

【讨论】:

  • 哇!非常感谢。工作完美,正是我需要的。如果不介意的话,试着解释一下你做了什么,这样我下次就不会再打扰你了,呵呵。
  • @PedroZancopé - 添加了一些解释 - 注意...我确实更改了代码,删除了 return await CoreServices.load 中的等待,因为等待是多余的
  • @PedroZancopé 也更改了 "OrderNumber": arrayOfOrders[i], 的代码,因为那是错误的 - 应该是 "OrderNumber": order, - 我看到你已经知道了!!谢谢
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2022-12-18
  • 2016-12-05
  • 1970-01-01
相关资源
最近更新 更多