Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to limit API calls per second with angular2

I have an API limit of 10 calls per second (however thousands per day), however, when I run this function (Called each Style ID of object, > 10 per second):

  getStyleByID(styleID: number): void {
    this._EdmundsAPIService.getStyleByID(styleID).subscribe(
      style => {this.style.push(style); },
      error =>  this.errorMessage = <any>error);
  }

from this function (only 1 call, used onInit):

  getStylesWithoutYear(): void {
    this._EdmundsAPIService.getStylesWithoutYear(this.makeNiceName, this.modelNiceName, this.modelCategory)
      .subscribe(
        styles => { this.styles = styles;
                      this.styles.years.forEach(year =>
                        year.styles.forEach(style =>
                          this.getStyleByID(style.id)));

        console.log(this.styles); },
        error =>  this.errorMessage = <any>error);
  }

It makes > 10 calls a second. How can I throttle or slow down these calls in order to prevent from getting a 403 error?

like image 951
Moshe Avatar asked Feb 15 '17 21:02

Moshe


People also ask

How do you call every 10 seconds in angular 8?

Answer: Use the JavaScript setInterval() method You can use the JavaScript setInterval() method to execute a function repeatedly after a certain time period.


2 Answers

I have a pretty neat solution where you combine two observables with the .zip() operator:

  1. An observable emitting the requests.
  2. Another observable emitting a value every .1 second.

You end up with one observable emitting requests every .1 second (= 10 requests per second).

Here's the code (JSBin):

// Stream of style ids you need to request (this will be throttled).
const styleIdsObs = new Rx.Subject<number>();

// Getting a style means pushing a new styleId to the stream of style ids.
const getStyleByID = (id) => styleIdsObs.next(id);

// This second observable will act as the "throttler".
// It emits one value every .1 second, so 10 values per second.
const intervalObs = Rx.Observable.interval(100);

Rx.Observable
  // Combine the 2 observables. The obs now emits a styleId every .1s. 
  .zip(styleIdsObs, intervalObs, (styleId, i) => styleId)
  // Get the style, i.e. run the request.
  .mergeMap(styleId => this._EdmundsAPIService.getStyleByID(styleId))
  // Use the style.
  .subscribe(style => {
    console.log(style);
    this.style.push(style);
  });

// Launch of bunch of requests at once, they'll be throttled automatically.
for (let i=0; i<20; i++) {
  getStyleByID(i);
}

Hopefully you'll be able to translate my code to your own use case. Let me know if you have any questions.

UPDATE: Thanks to Adam, there's also a JSBin showing how to throttle the requests if they don't come in consistently (see convo in the comments). It uses the concatMap() operator instead of the zip() operator.

like image 66
AngularChef Avatar answered Sep 29 '22 08:09

AngularChef


You could use a timed Observable that triggers every n milliseconds. I didn't adapt your code but this one shows how it would work:

someMethod() {
  // flatten your styles into an array:
  let stylesArray = ["style1", "style2", "style3"];

  // create a scheduled Observable that triggers each second
  let source = Observable.timer(1000,1000);
  // use a counter to track when all styles are processed
  let counter = 0;

  let  subscription = source.subscribe( x => {
    if (counter < stylesArray.length) {
        // call your API here
        counter++;
    } else {
        subscription.complete();
    }
  });
}

Find here a plunk that shows it in action

like image 44
Jan B. Avatar answered Sep 29 '22 08:09

Jan B.