Most people, including myself, meet observables for the first time when starting to develop Angular applications. Observables are the key elements of the framework, you can’t do too many things without using them. For example, HTTP requests return their results as an Observable. This way you can think that it is just another fancy variation for Promises and don’t use them for anything else. If you do this, sometimes weird things will happen (for example HTTP requests will be sent multiple times) and you will be missing many elegant solutions in your code. In this tutorial I’ll show how I managed to understand how Observables really work and help you make development with Angular more productive and relaxing.
Starting to look at HTTP requests in Angular as an alternative Promise implementation can be a good starting point and a misleading one as well. Their API is somewhat similar, as both provide a success and a failure callback for listening to results and errors.
We start the operation with a function call and the returned Observable/Promise emits the result/error later in time. This is where the similarities start and end. Everything else – execution, number of results and behavior – differs.
While a Promise only emits the result once, Observables can emit multiple values over time.
In the above example, the Observable emits the values 0,1,2,3,4 delayed by one second and then completes. The subscribe method is called five times. Besides multiple values, we can also detect the end of values. On completion the third callback gets called inside the subscribe function. After completion no more values are emitted.
Emitting values over time makes Observables very similar to streams (for example in Node.js). You might have found out that they also have similar methods like merging two separate streams or buffering (merge, buffer).
If you run this example, you will see that the value assigned inside the then callback is still undefined when it is printed with console.log. On the other hand, the value inside the subscribe callback won’t be undefined and it will be printed out by console.log.
This synchronous execution also goes for Subjects when calling the next method.
The resolved log will appear before the result in the console, because it iterates through all the subscriptions synchronously.
Have you experienced that things get weird when subscribing to an Observable multiple times? Like being executed multiple times, for example with an HTTP request?
It is because, when the subscribe method is called, a separate execution is created for the observable. And if that execution consists of an HTTP request, the endpoint will be called again.
We would expect that the second subscription (B), which arrives after 2 seconds, receives the same values as the first subscription. But in reality B gets the values from the start, just delayed with 2 seconds. The reason behind this is that every subscribe method creates a new execution, restarting the observable separately from the previous one.
Promises won’t restart when you write multiple then methods to the same promise, they just execute asynchronously and get the same value. To create the same behavior with Observables we have to apply the share operator, which gives the same execution for every subscription. In the background the operator creates a Subject and passes the values onto that.
While Promises has only the then method to mutate the returned value, Observables have multiple methods for it. These methods are named very similar to array methods.
Inside the then method you can either return a new value or a new promise. It acts the same, the next then method gets the value returned previously. With Observables we have to separate synchronous (map) and asynchronous (flatMap) transformation. Observables also have many array methods (filter, reduce, join, includes etc.) and array methods from utility libraries (Lodash: pluck, groupBy etc.).
Through Promises it is absolutely possible to understand Observables, you just have to know the differences:
- Multiple values over time
- Synchronous callbacks
- Multiple executions
- Array like methods
Hopefully the above comparisons have clarified the misunderstandings and obscure parts of Observables. For further learning I would recommend reading the blog of André Staltz (core contributor of RxJS) and listening to his tutorials on Egghead.
This article originally appeared on the Emarsys Craftlab blog.