Throttling And Debounce With Rxjs Observable
From the Rxjs overview, RxJS is a library for composing asynchronous and event-based programs by using observable sequences.
It has changed the way developers think when building applications, it allows to treat data as a stream as it changes through time, in other words:
everthing is a stream
In this article I am going talk about how to implement throttling and debouncing event from input fields, examples of its use cases are:
- (Debounce) You want to make sure that a user has finished performing an action before carrying out a task e.g wait for a user to finish typing into an input field before sending an ajax request to query the db.
- (Throttle) You want to control the rate at which an event is being fired so as to reduce the amount of time the corresponding task would run e.g you have an autcomplete field that queries your db and you want to trottle rate at which the input event is fired,(to avoid too many request to be sent in a short time).
For demo purposes I am going to create a simple app that shows how you can throttle and debounce input event of a text input field.
To start I would create a simple project that has the following folder structure:
|------
--- index.html
--- throttle.js
--- debounce.js
--- app.js
First we create our index.html file, and the content would look like this:
index.html
<!DOCTYPE html>
<html lang="">
<head>
<meta charset="utf-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>Throttle And Debouce With RxJs Observable</title>
<!-- Bootstrap CSS -->
<link href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/css/bootstrap.min.css" rel="stylesheet">
<style>
.m-t-10 {
margin-top: 10px;
}
</style>
</head>
<body>
<h1 class="text-center">Throttle And Debounce With Rxjs Observable</h1>
<div class="container">
<div class="row">
<div class="col-sm-6">
<h1>Throttle</h1>
<input type="text" id="throttle-field" class="form-control" placeholder="type to see output">
<h4>Output:</h4>
<hr>
<div class="m-t-10" id="throttle-output"></div>
</div>
<div class="col-sm-6">
<h1>Debounce</h1>
<input type="text" id="debounce-field" class="form-control" placeholder="type to see output">
<h4>Output:</h4>
<hr>
<div class="m-t-10" id="debounce-output"></div>
</div>
</div>
</div>
<script src="https://cdnjs.cloudflare.com/ajax/libs/rxjs/5.4.3/Rx.js"></script>
<script src="throttle.js"></script>
<script src="debounce.js"></script>
<script src="app.js"></script>
</body>
</html>
There's is nothing special here , I imported bootstrap css from a cdn for little styling. I also created two input fields and gave them unique IDs as well as a divs to output the values of the input fields.If you scroll to before the closing body tag I imported the rxjs library from a cdn and the other script file we would be creating.
Now as a refresher, to create an observable from an event we use the fromEvent()
method
var clickObservable = Rx.Observable.fromEvent(document.querySelector('button'), 'click');
and to track the event as it unfolds we subscribe to the observable
clickObservable.subscribe(() => console.log('Clicked!'));
This is similar in regular javascript as
var button = document.querySelector('button');
button.addEventListener('click', () => console.log('Clicked!'));
Next we create the throttle.js file, in here we create a function called throttle that takes three parameters(the query selector of the element, the event and the trottle time), it creates an observable from the event specified and throttles it with the specified time, but we are going to default the time to 2s(2000ms).
throttle.js
function throttle(el, event, time = 2000) {
// create a reference to the element
var element = document.querySelector(el);
// attach an observer
var eventObserver = Rx.Observable.fromEvent(element, event);
// call the throttleTime method on the observable passing the time,
// map tru the events an get the value of the input
// return an observable to be subscribed to
return eventObserver.throttleTime(time).map(event => event.target.value);
}
In the function, notice the throttleTime()
method, it is used to trottle the event fired on the input field, therefore the event is fired after every time
milliseconds specified. After which we map through the event and return the value of the input field.
Next we create the debounce.js file, in here we create a function called debounce that takes three parameters(the query selector of the element, the event and the debounce time), it creates an observable from the event specified and debounce until the specified time has elapsed after the event was fired, but we are going to default the time to 2s(2000ms).
debounce.js
function debounce(el, event, time = 2000) {
// create a reference to the element
var element = document.querySelector(el);
// attach an observer
var eventObserver = Rx.Observable.fromEvent(element, event);
// call the debounveTime method on the observable passing the time,
// map tru the events an get the value of the input
// return an observable to be subscribed to
return eventObserver.debounceTime(time).map(event => event.target.value);
}
In the function, notice the debounceTime()
method, it is used to debounce the event fired on the input field,therefore the event is fired time
milliseconds after the user has finished typing. After which we map through the event and return the value of the input field.
The usual solution is a debouncing device or software that ensures that only one digital signal can be registered within the space of a given time (usually milliseconds).
Right now whats's left is to call the functions and subscribe to the return observables. This would be done in the app.js file.
app.js
throttle('#throttle-field', 'input').subscribe(value =>
print('#throttle-output', value)
);
debounce('#debounce-field', 'input', 500).subscribe(value =>
print('#debounce-output', value)
);
function print(el, val) {
if (!val) return;
var data = document.createElement('pre');
data.innerHTML = val;
document.querySelector(el).appendChild(data);
}
I created a helper print
function to append the specified value to the specified element.
We can now open the index.html file in the browser to test
The link the full project can be found here
Thank You. Please Feel free to give your opinion about the article regarding additions or omitions, follow me on twitter
@abolaji
I plan to write an implementation in Angular soon.. Cheers
Nice