A brief intro to RxJs

4 min read
From promises to observables.
Post cover image

RxJs is the implementation of Reactive Extensions for Javascript. These extensions are a collection of tools allowing us to write declarative reactive code as opposed to imperative (e.g. do this when x instead of if x is this right now, then do the following...). Basically, it gives us operators (functions) that can enhance handling of event-based logic, especially when working with multiple correlated events.

Rx definitely has a learning curve. New concepts and and terms can be overwhelming at the start. But once you try enough times, eventually it clicks and you get much faster at learning the all the wizzabanga words and concepts. I'll try to keep this post as simple as possible, but you definitely need to be comfortable with the promises API.

Promises vs Observables

Imagine we have an array of strings describing our thoughts.

const thoughts = ["food", "sleep", "code"]

Promise

When we work with events in JavaScript we will typically uses the Promise API. For a promise, two things can happen:

1- It resolves with a single value.
2- It rejects with a single value, usually an error message.

Both 1 and 2 signal the completion of a promise.

const thoughtsPromise = () => {
  return new Promise((resolve, reject) => {
    resolve(thoughts)
  })
}

Observable

An observable is a stream of data from which we can get notifications with values. We can get those notifications in three different scenarios:

1- When there is a new value from the stream.
2- When an error occurs giving us the error value.
3- When the stream is complete.

One difference is that observables can resolve many times with new values. For example, imagine you want to show a video on your app to the user. Would it be better to let the user download the entire video all at once, or to stream it little by little? Observables help you when working with streams of data.

Let's create an observable.

The from operator can transform data such as arrays or promises into observables.

import { from } from "rxjs"
e trailing $ sign is a naming convention for observables in JS
cons
const thoughts$ = from(thoughts)

That's it!

Getting Data

Back to our promise. What do you suppose happens when thoughtsPromise resolves?

const getValue = async () => {
  try {
    const thoughtsArray = await thoughtsPromise()
    console.log(thoughtsArray)
    // Output: ["food", "sleep", "code"]
  } catch (err) {
    // handle error
  }
}

We got the array all at once. And what do you think happens when we start to listen for values, in other words subscribe to our thoughts$ observable?

// We subscribe to an observable to get values from it
thoughts$.subscribe(
  (value) => console.log(value),
  (err) => null, //handle error,
  () => null, // handle completion,
)
// Output:
//    food
//    sleep
//    code

We get the values from the array one by one. A stream of data. Cool.

Play with this example

Do I have to know Rx?

Nope. But here are some use cases to consider:

  1. To keep code flat in JS if you can't use async await for some reason.

  2. To handle complex event-based logic whether its network related or UI related (e.g. websockets / drag and drop).

  3. If your teammates come from different languages but you all know Rx, it might be easier for them to just get going with RxJs than to use promises and async await.

  4. In other languages, Rx is super useful for handling multi-threading.

  5. If you enjoy reactive programming and want to apply it everywhere, go ahead 🙂.

Debouncing user input
Discuss on Bluesky