RxJs: Reducing number of API Calls to your server using debounceTime
Exploring debounceTime in RxJs
This article assumes you have a decent knowledge of Observables. If you are new to Observables I highly recommend checking out the link below by Ashish Singh.
Recently, I was working on a small project where I had to hit third-party API to get search results whenever a user starts entering some text in the input box. Something like real-time searches by Google.
Recently, I was working on a small project where I had to hit third-party API to get search results whenever a user starts entering some text in the input box. Something like real-time searches by Google.
While I was implementing this feature I found an interesting behavior in the above approach and also discovered a new cool operator, which I would like to share with you in this blog post.
Let’s get started.
So, I created a small example where I have an input box and an Observable watching that input box and on every
keyup
event, it will emit an KeyBoardEvent
from which we will extract the value entered by the user using map operator.
Further, On Subscribing to this Observable, we will pass every value emitted from the observable and pass it to a function called
sendValues
which will for now append every result to innerHtml of a div with id #results
.
HTML Code:
<!DOCTYPE html> <html> <head> <meta charset="utf-8"> <meta name="viewport" content="width=device-width"> <title>JS Bin</title> </head> <body> <script src="https://unpkg.com/@reactivex/rxjs@5.0.0-beta.7/dist/global/Rx.umd.js"></script> Input: <input type="text" id="textInput"> <h4>Values are</h4> <div id="results"></div> </body> </html>
JS Code :
# I think the code is self explaining... var input = document.getElementById('textInput');
var input$ = Rx.Observable .fromEvent(input, 'keyup') .map(x => x.currentTarget.value)
input$.subscribe(x => sendValues(x));
function sendValues(x){ var pre = document.createElement('pre'); pre.innerHTML = JSON.stringify(x); document.getElementById('results').appendChild(pre); }
Output:
Now, if you see carefully, you can see that as I type in the input box values are emitted immediately. Now, imagine if the output was going to API instead of printing it to the screen. Our API would be loaded with unnecessary requests, first with ‘H’ than ‘He’ then ‘Hel’ then ‘Hell’ and so on… Yes, it would be a real HELL for our API server and before you know you have finished your daily API Limit in a blink of an eye.
What we want here to fix this problem is a rate limiter which will request API only after a specific interval of time.
DebounceTime to the Rescue :
Note: I am using RxJs 5 in whichdebounce
is migrated todebounceTime
. If you are using earlier version you should usedebounce
instead.
Now, this time we have implemented the same example using RxJs operator
debounceTime.
But, let’s first understand what debounce is, according to the RxJs documentation the signature of
debounceTime
is :Signature:
debounceTime(dueTime: number, scheduler: Scheduler): Observable
And it says that the operator is used to :
Discarding emitted values that take less than the specified time between output
The Description wasn't quite good huh… Don’t worry I will try to elaborate it in layman’s term but first, let’s see if it works.
JS Code:
// Same Example by using DebounceTime with delay of 1 sec.
var input = document.getElementById('textInput');
var input$ = Rx.Observable .fromEvent(input, 'keyup') .map(x => x.currentTarget.value) .debounceTime(1000)
input$.subscribe(x => sendValues(x));
function sendValues(x){ var pre = document.createElement('pre'); pre.innerHTML = JSON.stringify(x); document.getElementById('results').appendChild(pre); }
Output:
Wow, seems like
debounceTime
worked perfectly and that’s exactly what we wanted. Hurray!!!!So, How does debounceTime operator work and what is debouncing exactly?
Note: debouncing is a programming concept while debounceTime is a RxJs operator which implements this concept. Just to clear the confusion,
debounceTime in simple Terms :
If you are familiar with Observable then below explanation, straight from the source will be self-explaining.
If not you might want to check to debounce in simple terms and Layman’s example for more detailed explanation.
So, this is what it says :
debounceTime
emits a value from the source Observable only after a particular time span has passed without another source emission.It’s like delay, but passes only the most recent value from each burst of emissions.
debounceTime
delays values emitted by the source Observable but drop previous pending delayed emissions if a new value arrives from the source Observable.
This operator does keeps track of the most recent value from the source Observable, and emits that only when `dueTime` enough time has passed without any other value appearing on the source Observable.
If a new value appears before `dueTime` silence occurs, the previous value will be dropped and will not be emitted on the output Observable.
Debouncing in Simple Terms:
Debouncing will prevent a function from running while it is still being called frequently (In this case calling API for every keyup event).
Further, A debounced function will only run after it has been determined that it is no longer being called, at which point it will run exactly once.
Which again means, the original function(API Hit Function) be called after the caller stops calling the decorated function(Debounce Function) after a specified period.
Real World Layman’s Example :
To Explain it in a real world layman example, suppose you have a friend say, Mandy.
Mandy likes to chat with you on facebook instant messenger but she is a quite talkative person and sends a new message every 5 seconds.
Now while your facebook messenger is sending your notification again and again, in this situation you can follow two approaches.
Naive Approach: Check every message as long as it arrives, whenever you get a notification, you go and check. Not a very efficient way but you stay up to date.
Debounce Approach: Over a period of time you figure out, that Mandy breaks down the whole story into small parts and sends them one after the other.
Now, you wait for Mandy to finish up her whole story and if she stops sending you messages for say 5 minutes, you assume that she has finished and now you check all her messages.
Pretty Simple!!!
Conclusion:
To conclude about what we learned is that if you have a function that gets called every time an event fires and you don’t want this behavior, instead you want to rate limit the frequency of call to the function then you should give debounce a shot.
Comments
Post a Comment