Search⌘ K
AI Features

Querying the Sequence

Explore how RxJS Observables transform event handling by treating streams of events like queryable data sequences. Understand how to avoid external state and simplify asynchronous code by using Observables to filter, transform, and manage streams such as mouse clicks efficiently.

We'll cover the following...

Let’s implement a simple version of that mouse stream using traditional event listeners in JavaScript. To log the x- and y-coordinates of mouse clicks, we could write something like this:

Javascript (babel-node)
const registerClicks = e =>
{
console.log(e.clientX, e.clientY);
};
document.body.addEventListener("click", registerClicks);

The problem, of course, is that manipulating events is not as easy as manipulating arrays. For example, if we want to change the preceding code so it logs only the first ten clicks on the right side of the screen (quite a random goal, but bear with me here), we would write something like this:

Javascript (babel-node)
var clicks = 0;
document.addEventListener('click', function registerClicks(e)
{
if (clicks < 10)
{
if (e.clientX > window.innerWidth / 2)
{
console.log(e.clientX, e.clientY);
clicks += 1;
}
}
else
{
document.removeEventListener('click', registerClicks);
}
});

To meet our requirements, we introduced an external state through a global variable clicks that counts the clicks made so far. We also need to check for two different conditions and use nested conditional blocks. And when we’re done, we have to tidy up and unregister the event to not leak memory.

We managed to meet our easy requirements, but ended up with a complicated code for a simple goal. It’s a difficult code to maintain and not obvious for a developer who looks at it for the first time. More importantly, we made it prone to develop subtle bugs in the future because we need to keep the state.

All we want in this situation is to query the “database” of clicks. If we were dealing with a relational database, we’d use the declarative language SQL:

MySQL
SELECT x, y FROM clicks LIMIT 10

What if we treated that stream of click events as a data source that can be queried and transformed? After all, it’s no different from a database—it is one that emits values in real-time. All we need is a data type that abstracts the concept for us.

Enter RxJS and its Observable data type:

Javascript (babel-node)
var Rx=require('rx');
Rx.Observable.fromEvent(document, 'click')
.filter(function(c)
{
return c.clientX > window.innerWidth / 2;
}) .take(10)
.subscribe(function(c)
{
console.log(c.clientX, c.clientY)
})

This code reads like this:

Note: Create an Observable of click events and filter out the clicks that happen on the left side of the screen. Then print the coordinates of only the first ten clicks to the console as they happen.

Notice how the code is easy to read even if we’re not familiar with it. Also, there’s no need to create external variables to keep state, which makes the code self-contained and makes it harder to introduce bugs. There’s no need to clean up after yourself either, so there is no chance of us introducing memory leaks by forgetting about unregistering event handlers.

In the preceding code, we created an Observable from a DOM event. An Observable provides us with a sequence or stream of events that we can manipulate as a whole instead of as a single isolated event each time. Dealing with sequences gives us enormous power. We can merge, transform, or pass around Observables easily. We’ve turned events we can’t get a handle on into a tangible data structure that’s as easy to use as an array, but much more flexible.