What is RxJS?
Reactive programming is an asynchronous programming paradigm concerned with data streams and the propagation of change - Wikipedia
RxJS is a library for reactive programming using observables that makes it easier to compose asynchronous or callback-based code - RxJS docs
The essential concepts in RxJS are
- An Observable is a stream of data
- Observers can register up to 3 callbacks:
- next is called 1:M time to push new values to the observer
- error is called at most 1 time when an error occurred
- complete is called at most 1 time on completion
- Subscription "kicks off" the observable stream
Without subscribing the stream won't start emitting values. This is what we call a cold observable.
It's similar to subscribing to a newspaper or magazine... you won't start getting them until you subscribe. Then, it creates a 1 to 1 relationship between the producer (observable) and the consumer (observer).
What are RxJS operators?
Operators are pure functions that enable a functional programming style of dealing with collections with operations. There are two kinds of operators:
- Creation operators
- Pipeable operators: transformation, filtering, rate limiting, flattening
Subjects are a special type of Observable that allows values to be multicast to many Observers. While plain Observables are unicast (each subscribed Observer owns an independent execution of the Observable), Subjects are multicast. This is what we call a hot observable.
In this article, I will focus on the
forkJoin operators. These are RxJS combination operators, which means that they enable us to join information from multiple observables. Order, time, and structure of emitted values are the primary differences among them.
Let's look at each one individually.
zipdoesn’t start to emit until each inner observable emits at least one value
zipemits as long as emitted values can be collected from all inner observables
zipemits values as an array
Let’s imagine that you are with Mario and Luigi, two of your best friends, at the best Italian restaurant in Rome. Each one of you orders a drink, a pizza, and a dessert. You specify to the waiter to bring the drinks first, then the pizzas, and finally the desserts.
This situation can be represented with 3 different observables, representing the 3 different orders. In this specific situation, the waiter can use the
zip operator to bring (emit) the different order items by category.
If you go back to the same Italian restaurant with your girlfriend, but she doesn’t want to eat, this is what will happen:
waiter$ uses the
zip operator, you will only get your drink!
Because, when the
waiter$ emits the drinks, the
girlfriend$ observable is complete and no more value can be collected from it. Hopefully, the
waiter$ can use another operator for us so we don't break up with our girlfriend ?
combineLatestdoesn’t start to emit until each inner observable emits at least one value
- When any inner observable emits a value, emit the last emitted value from each
At the exact same restaurant, the smart
waiter$ now decide to use
combineLatest, the order of the provided inner observables does matter.
you$ is provided first to
waiter$, it will emit only one value
This is happening because
combineLatest doesn’t start to emit until each inner observable emits at least one value.
girlfriend$ starts emitting when the first inner observable emits its last value. Then,
combineLatest emits the last values collected from both inner observables.
forkJoinemits the last emitted value from each inner observables after they all complete
forkJoinwill never emit if one of the observables doesn’t complete
When you go to the restaurant and order for a pizza, you don’t want to know all the steps about how the pizza is prepared. If the cheese is added before the tomatoes or the opposite. You just want to get your pizza! This is where
forkJoin comes into play.
- If one of the inner observables throws an error, all values are lost
- If you are only concerned when all inner observables complete successfully, you can catch the error from the outside
- If you don’t care that inner observables complete successfully or not, you must catch errors from every single inner observable
Personally, when I go to a restaurant with friends, I don’t care if one of them receives a burnt pizza. I just want mine ? so I will ask the
waiter$ to catch the errors from inner observables individually.
We covered a lot in this article! Good examples are important to better understand RxJS operators and how to choose them wisely.
For combination operators like
forkJoin the order of inner observables that you provide is also critical, as it can drives you to unexpected behaviours.
There is much more to cover within RxJS and I will do it in further articles.
I hope you enjoyed this article! ?
? You can follow me on Twitter to get notified about new Angular/RxJS blog posts and cool tips!