Javascript 如何限制返回输出的typescript函数

Javascript 如何限制返回输出的typescript函数,javascript,node.js,typescript,throttling,delayed-execution,Javascript,Node.js,Typescript,Throttling,Delayed Execution,我正在使用typescript编写node.js应用程序。我的应用程序将有几个相互通信的服务。一些服务需要调用外部API。此API对每秒可执行的调用数有限制。因此,我想创建一个封装外部API调用的服务(我们称之为ApiService)。其他服务将调用此服务,它将在队列中收集它们的请求并按顺序执行它们-每秒N个请求(为简单起见,假设每秒1个)。当服务A调用ApiService的一个方法时——它期望收到一个输出(收到一个承诺就可以了) 现在我的问题是-如何在ApiService中对这些API调用进行

我正在使用typescript编写node.js应用程序。我的应用程序将有几个相互通信的服务。一些服务需要调用外部API。此API对每秒可执行的调用数有限制。因此,我想创建一个封装外部API调用的服务(我们称之为ApiService)。其他服务将调用此服务,它将在队列中收集它们的请求并按顺序执行它们-每秒N个请求(为简单起见,假设每秒1个)。当服务A调用ApiService的一个方法时——它期望收到一个输出(收到一个承诺就可以了)

现在我的问题是-如何在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
   }
}
该服务:

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
   }
}
我面临的问题是,当RetrieveA方法返回func()时,函数将被执行。我需要返回一个承诺,但实际的函数执行需要在RunQueue()方法中进行。有没有办法做到这一点?我是否可以在不立即执行函数的情况下返回一个承诺,并且在等待该承诺时,即在RunQueue方法中调用函数时接收输出

或者,有没有一种不同的方法来解决我对返回输出的API调用进行节流的问题


我是Node.js/Typescript/JavaScript领域的新手,因此非常感谢您的帮助:)

我确实找到了一个有效的解决方案。我不太熟悉JavaScript中的整个Promise和async概念,因此这可能不是最好的解决方案,但它适合我的具体情况。下面是其他希望实现类似功能的人的代码:

示例服务A与上面的相同:

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 {
   // We keep the functions that need to be executed in this queue
   // and process them sequentially
   queue: (() => void)[] = [];

   public async RetrieveA(param1, param2): Promise<number> {
      // This resolver serves two purposes - it will be called when the
      // function is executed (to set the output), but will also be part
      // of the Promise that will be returned to the caller (so that the
      // caller can await for the result).
      let resolver: (value: number) => void;

      // This function will be executed by the RunQueue method when its
      // turn has come. It makes a call to the external API and when that
      // call succeeds - the resolver is called to return the result through
      // the Promise.
      const func = async () => {
         return this.CallApi(param1, param2).then(resolver);
      };

      this.queue.push(func);

      // This is the promise that we return to the caller, so that he
      // can await for the result. 
      const promise = new Promise<number>((resolve, reject) => {
         resolver = resolve;
      });

      return promise;
   }

   public async Run() {
      this.RunQueue(this.queue);
   }

   private async RunQueue(funcQueue: (() => void)[]) {
      // Get the first element of the queue
      const func = funcQueue.shift();

      // If the queue is empty - this method will continue to run
      // until a new element is added to the queue
      if (func) {
         await func();
      }

      // Recursively call the function again after 1 second
      // This will process the next element in the queue
      setTimeout(() => {
         this.RunQueue(funcQueue);
      }, 1000);
   }

   private async CallApi(param1, param2): Promise<number> {
      // Call the external API, process its output and return
   }
}
导出类API服务{
//我们将需要执行的函数保留在此队列中
//并按顺序进行处理
队列:(()=>void)[]=[];
公共异步检索(param1,param2):承诺{
//此解析器有两个用途-当
//函数被执行(以设置输出),但也将是一部分
//将返回给调用方的承诺(以便
//调用方可以等待结果)。
让解析器:(值:number)=>void;
//此函数将由RunQueue方法在
//轮到了。它调用外部API,并在何时调用
//调用成功-调用解析器以通过返回结果
//承诺。
const func=async()=>{
返回此.CallApi(param1,param2)。然后返回(解析器);
};
this.queue.push(func);
//这是我们对来电者的承诺,让他
//我们可以等待结果。
持续承诺=新承诺((解决、拒绝)=>{
分解器=分解;
});
回报承诺;
}
公共异步运行(){
this.RunQueue(this.queue);
}
专用异步运行队列(funcQueue:(()=>void)[]){
//获取队列的第一个元素
常量func=funcQueue.shift();
//如果队列为空-此方法将继续运行
//直到将新元素添加到队列中
if(func){
等待函数();
}
//1秒后再次递归调用该函数
//这将处理队列中的下一个元素
设置超时(()=>{
this.RunQueue(funcQueue);
}, 1000);
}
专用异步调用API(param1,param2):承诺{
//调用外部API,处理其输出和返回
}
}

我希望代码中的注释能够清楚地说明我想要实现什么(以及如何实现)。

如果您想将RetreiveA调用限制为每秒2次,那么所有这些都可以简单得多:

//lib is here: https://github.com/amsterdamharu/lib/blob/master/src/index.js
import * as lib from '../../src/index'
const twoPerSecond = lib.throttlePeriod(2,1000);
export class ApiService {
  public RetrieveA(param1, param2): Promise<number> {
    //removed the resolver part, according to the typescript signature
    //  it should return a promise of number but resolver actually takes
    //  that number and returns void (undefined?)
    return twoPerSecond(this.CallApi.bind(this))([param1, param2]);
  }
  //change the signature of this function to take one parameter
  //  but deconsruct the array to param1 and param2
  private async CallApi([param1, param2]): Promise<number> {
    // Call the external API, process its output and return
  }
}
//lib在这里:https://github.com/amsterdamharu/lib/blob/master/src/index.js
从“..//src/index”导入*作为库
常数twoPerSecond=lib.throttlePeriod(21000);
出口级服务{
公开检索(param1,param2):承诺{
//根据typescript签名,已删除冲突解决程序部分
//它应该返回一个数字的承诺,但解析器实际需要
//该数字并返回void(未定义?)
返回twoPerSecond(this.CallApi.bind(this))([param1,param2]);
}
//将此函数的签名更改为采用一个参数
//但将数组解构为param1和param2
专用异步调用API([param1,param2]):承诺{
//调用外部API,处理其输出和返回
}
}

您的方法仅在此类只有一个实例时有效。如果要创建多个实例并在这些实例上调用
RetrieveA
,则不再将请求限制为
callApi

,如果有许多项需要异步结果,则可能会有所帮助。您可以限制每个时段的活动承诺或承诺量(比如每秒不超过10个)。如果你真的有很多数据要处理,那么代码会显示你每批处理1000个数据块。你可以使用a作为数据源,将其批量增加到每批1000个,并限制有效承诺的数量。我不确定我是否完全理解你的建议。我不知道如何将承诺返回给ApiService的调用者,但执行在任意时间点计算它的方法。此外,我没有太多的项目,例如大约每10秒30个,对我来说重要的是每秒执行的次数不超过2个。我觉得我在任何地方都会对此进行评论,但我更喜欢
const delay=time=>new Promise(res=>setTimeout(res,time))
然后使用
wait delay(1000)
:)这看起来比我的好:)谢谢你的建议
//lib is here: https://github.com/amsterdamharu/lib/blob/master/src/index.js
import * as lib from '../../src/index'
const twoPerSecond = lib.throttlePeriod(2,1000);
export class ApiService {
  public RetrieveA(param1, param2): Promise<number> {
    //removed the resolver part, according to the typescript signature
    //  it should return a promise of number but resolver actually takes
    //  that number and returns void (undefined?)
    return twoPerSecond(this.CallApi.bind(this))([param1, param2]);
  }
  //change the signature of this function to take one parameter
  //  but deconsruct the array to param1 and param2
  private async CallApi([param1, param2]): Promise<number> {
    // Call the external API, process its output and return
  }
}