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?
Answer: Use the JavaScript setInterval() method You can use the JavaScript setInterval() method to execute a function repeatedly after a certain time period.
I have a pretty neat solution where you combine two observables with the .zip() operator:
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.
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
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With