【发布时间】:2018-06-14 10:26:00
【问题描述】:
我正在使用 typescript 编写一个 node.js 应用程序。我的应用程序将有多个相互通信的服务。一些服务需要调用外部 API。此 API 对每秒可以执行的调用次数有限制。因此,我想创建一个包装外部 API 调用的服务(我们称之为 ApiService)。其他服务将调用此服务,它将在队列中收集它们的请求并按顺序执行它们 - 每秒 N 个请求(为简单起见,我们假设每秒 1 个)。当服务 A 调用 ApiService 的方法时 - 它期望接收输出(可以接收 Promise)。
现在我的问题是 - 如何在 ApiService 中对这些 API 调用进行排队,以便每 1 秒执行一次队列中的下一个调用,并将该 API 调用的输出返回给 ApiService 的调用者?
这是一个示例服务:
export class ServiceA {
apiService: ApiService;
public constructor(_apiService: ApiService) {
apiService = _apiService;
}
public async DoWork() {
// Do some stuff
const output: number = await apiService.RetrieveA(param1, param2);
// Do something with the output
}
}
ApiService:
export class ApiService {
queue: (() => Promise<any>)[] = [];
public async RetrieveA(param1, param2): Promise<number> {
const func = async () => {
return this.CallApi(param1, param2);
};
this.queue.push(func);
return func();
}
public async RunQueue() {
while(true) {
const func = this.queue.shift();
if (!func) { continue; }
// Call the function after 1 second
await setTimeout(() => { func(); }, 1000);
}
}
private async CallApi(param1, param2): Promise<number> {
// Call the external API, process its output and return
}
}
编排整个事情的主要方法:
var CronJob = require('cron').CronJob;
const apiService = new ApiService();
const service = new ServiceA(apiService);
new CronJob('* * * * * *', function() {
service.DoWork();
}, null, true);
apiService.RunQueue();
我面临的问题是,当 RetrieveA 方法返回 func() - 函数被执行。我需要返回一个 Promise,但实际的函数执行需要在 RunQueue() 方法中进行。有没有办法做到这一点?我可以在不立即执行该函数的情况下返回一个 Promise 并等待该 Promise - 在 RunQueue 方法中调用该函数时接收输出吗?
或者是否有其他方法可以解决我限制返回输出的 API 调用的问题?
我是 Node.js/Typescript/JavaScript 世界的新手,因此感谢您提供任何帮助 :)
【问题讨论】:
-
如果您有很多项目要异步结果,那么following answer 可能会有所帮助。您可以限制每个周期的活动承诺或承诺数量(例如每秒不超过 10 个)。如果您确实有大量数据要处理,那么该代码会显示您将其分块至每批 1000 个。您可以使用stream 作为数据源,将其批量化为每批 1000 个,并限制活动承诺的数量。
-
不确定我是否完全理解您的建议。我不知道如何将 Promise 返回给我的 ApiService 的调用者,但在任意时间点执行计算它的方法。另外我没有很多项目,例如每 10 秒大约 30 个,对我来说重要的是我每秒执行不超过 2 个。
标签: javascript node.js typescript throttling delayed-execution