Angular Experiences: Promises Vs. Observables – Choose Your Destiny

Whether to use a Promise or an Observable is a valid question. Let me show you a little hint for deciding when to use what.

In diesem Artikel:

TH-round
Thomas Hilzendegen ist Consultant bei Thinktecture und hast sich auf Angular fokussiert.

What are Promises?

Fun Fact: Promises are a commitment for value over time; they promise this.

But let me be more precise:

There was a time in JavaScript before Promises. It was a dark age. Every developer was scared to end in hell – the callback hell. A callback is a function called by a function to continue when something (maybe asynchronous) completes (please note, I am already using fat-arrow functions here, which is a much later introduced language feature):

				
					someMethod(input, output => {
  nextMethod(output, more => {
    hopefullyFinalMethod(more, realval => {
        dangThereIsMoreMethod(realval, () => alert('Now we are done!'));
    });
  });
});
				
			

This is just a small example – there are way deeper ones in the wild. Of course, you may define functions for all those callbacks and use them instead of the inline one. But things get complicated when the call context of the function needs to stay (e.g. this).

Rewriting the example above with Promises, presupposed the methods are capable of supporting this, we should get more arranged code:

				
					someMethod(input)
    .then(output => nextMethod(output))
    .then(mode => hopefullyFinalMethod(more))
    .then(realval => dangThereIsMoreMethod(realval))
    .then(() => alert('Now we are done!'));
				
			

You see, this is much more straightforward regarding the indentation. But still, you may notice the readability of the chained method calls is not yet the way it should be.

When async/await was available, you could start to write asynchronous code like it would be synchronous code by using the appropriate keywords:

				
					const output = await someMethod(input);
const more = await nextMethod(output);
const realval = await hopefullyFinalMethod(more);
await dangThereIsMoreMethod(realval);
alert('Now we are done!');
				
			

This code should be the final form – much more readable and easily understandable as you see the code flow immediately. Using Observables here would harm the readability instead if there isn’t a need for them at all.

Bonus: In case of async/await, the error handling is just a simple try/catch instead:

				
					try {
    const result = await asyncMethod();
} catch {
    alert('An error occured!');
}
				
			

What are Observables?

Speaking of Observables – what are they? In its simplest form, it can be like a Promise. It provides one value over time. But Observables are much more than this. An Observable is capable of delivering multiple values over time – it’s like streaming. It can be canceled or, in case of errors, easily retried. Numerous Observables can be combined, or there can be a race to have only the first used. The RxJS framework for Observables is a mighty one.

Furthermore, Observables need to have a subscription to start emitting values. Otherwise, they are just a blueprint of code handling future emits. If you create your own “producer” of values, you can react on subscribe and unsubscribe. You can then start, stop, or tear down the emit of values.

Creating your producer of an Observable is as simple as instantiating a new instance of it:

				
					const observable = new Observable(subscriber => {
    // ...
    // put your value-producing logic here
    // ...
    return () => {
        // ...
        // put your teardown logic here
        // ...
    };
});
				
			

The given “subscriber” instance has the method next(value) to provide values. In case of an error, the method error(error) should be called. And, when there are no more values, the method complete() should be finally used.

Where Promises may be the Better Choice

One rule-of-thumb could be that everywhere you want only one value over time, e.g., from an HTTP request, you should use a Promise. Usually, this shouldn’t be a problem if you accept the following drawbacks:

Drawbacks

  • You cannot easily cancel a Promise.
  • You cannot easily repeat or retry a Promise.

Why could these drawbacks be a problem for you? Let’s assume that you are developing an Angular application. You usually request some data when the user navigates to your component. When this takes a while, and the user navigates away, the HTTP request continues and completes by downloading all the “useless” data. Of course, this could somehow be archived by implementing some complex logic or use an already built framework for this.

On the other side, to repeat or retry a Promise, you also need some even more complex logic to achieve it (or use a library, if available).

Where Observables may be the Better Choice

The first argument for using Observables is handling streaming data – multiple values over time. Without them, you would need to have a callback registering logic to inform others about new values. But this may be just the simple form of it. Starting with more complex scenarios where, e.g., retrying, repeating, or canceling is a topic, you may get lost in writing so much code instead of using an operator function.

For deeper information about Observables, you may have a look at my colleague Yannick Baron’s three-part webinar series (in German), starting with part one RxJS von Grund auf: Einführung in reaktives JavaScript.

But sometimes, even when only one value is available, building an Observable pipe could be the better choice if the code would be much more complex without it.

Drawbacks

  • You have to learn a complex framework.
  • You could tend to use Observables everywhere.

As mentioned earlier, the RxJS framework is a complex one. There are so many operators and possibilities to work with that reactive programming style.

Conclusion

With great power, there must also come great responsibility. It would be best not to use Observables as an answer for any problem coming up. Code can look way more difficult to understand when Observable pipes get significant, or the reactive way is unnecessary at this particular point in the code.

Kostenloser
Newsletter

Aktuelle Artikel, Screencasts, Webinare und Interviews unserer Experten für Sie

Verpassen Sie keine Inhalte zu Angular, .NET Core, Blazor, Azure und Kubernetes und melden Sie sich zu unserem kostenlosen monatlichen Dev-Newsletter an.

Newsletter Anmeldung
Diese Artikel könnten Sie interessieren
Angular
sl_300x300

View Transition API Integration in Angular—a brave new world (Part 1)

If you previously wanted to integrate view transitions into your Angular application, this was only possible in a very cumbersome way that needed a lot of detailed knowledge about Angular internals. Now, Angular 17 introduced a feature to integrate the View Transition API with the router. In this two-part series, we will look at how to leverage the feature for route transitions and how we could use it for single-page animations.
15.04.2024
Low-angle photography of metal structure
AI
cl-neu

AI-Funktionen zu Angular-Apps hinzufügen: lokal und offlinefähig

Künstliche Intelligenz (KI) ist spätestens seit der Veröffentlichung von ChatGPT in aller Munde. Wit WebLLM können Sie einen KI-Chatbot in Ihre eigenen Angular-Anwendungen integrieren. Wie das funktioniert und welche Vor- und Nachteile WebLLM hat, lesen Sie hier.
26.02.2024
Angular
sl_300x300

Konfiguration von Lazy Loaded Angular Modulen

Die Konfigurierbarkeit unserer Angular-Module ist für den Aufbau einer wiederverwendbaren Architektur unerlässlich. Aber in der jüngsten Vergangenheit hat uns Angular seine neue modullose Zukunft präsentiert. Wie sieht das Ganze jetzt aus? Wie konfigurieren wir jetzt unsere Lazy-Komponenten? Lasst uns gemeinsam einen Blick darauf werfen.
03.08.2023
Angular
YB_300x300

Using EntityAdapter with ComponentStore: @ngrx/entity Series – Part 3

As someone who enjoys the ComponentStore on an average level, I have written simple reactive CRUD logic several times. While storing a vast number of entities in the component state might not be a frequent use case, I will briefly illustrate the usage of the EntityAdapter with the @ngrx/component-store.
14.02.2023
Angular
YB_300x300

Multiple Entity Collections in the Same Feature State: @ngrx/entity-Series – Part 2

After introducing the @ngrx/entity package, I am often asked how to manage multiple entity types in the same feature state. While I hope that the previous part of this article series has made this more apparent, I will further focus on this question in the following.
07.02.2023
Angular
YB_300x300

Managing Your Collections With the EntityAdapter: @ngrx/entity-Series – Part 1

This three-part series of blogposts is targeted at developers who have already gained experience with NgRx but still manage their collections themselves. In the first part I introduce the Entity Adapter, in the second part I show you how to connect it to NgRx and in the third part how to do it with the Component Store as well.
31.01.2023