What is the difference between Promise
and Observable
in Angular?
An example on each would be helpful in understanding both the cases. In what scenario can we use each case?
What is the difference between Promise
and Observable
in Angular?
An example on each would be helpful in understanding both the cases. In what scenario can we use each case?
Promise
A Promise
handles a single event when an async operation completes or fails.
Note: There are Promise
libraries out there that support cancellation, but ES6 Promise
doesn't so far.
Observable
An Observable
is like a Stream
(in many languages) and allows you to pass zero or more events where the callback is called for each event.
Often Observable
is preferred over Promise
because it provides the features of Promise
and more. With Observable
it doesn't matter if you want to handle 0, 1, or multiple events. You can utilize the same API in each case.
Observable
also has the advantage over Promise
to be cancellable. If the result of an HTTP request to a server or some other expensive async operation isn't needed anymore, the Subscription
of an Observable
allows you to cancel the subscription, while a Promise
will eventually call the success or failed callback even when you don't need the notification or the result it provides anymore.
While a Promise
starts immediately, an Observable
only starts if you subscribe to it. This is why Observables are called lazy.
Observable provides operators like map
, forEach
, reduce
, ... similar to an array
There are also powerful operators like retry()
, or replay()
, ... that are often quite handy.
A list of operators shipped with rxjs
Lazy execution allows you to build up a chain of operators before the observable is executed by subscribing, to do a more declarative kind of programming.
Observable
, or why do you think the arguments for Observable
are not valid or not strong enough? –
Madelaine Http
API might change a bit over time and make better use of Observables
. Http
is still quite basic. –
Madelaine toPromise
is a bad idea. Only if you are using Observables
everywhere in a reative-like programming style, this really can show its potential. Thanks a lot for your feedback. I didn't perceive it as criticism. I'm just curious what you learned and perhaps can share something new to me. –
Madelaine Observable
is an advantage over Promises
? When using promises, I can chain them and control whether the next promise in the chain should be fired or not. –
Alesha Promise
(ES6) can't be cancelled while waiting for an async call to complete, while Observable does. This cancellation might propagate back to the producer of the async result and might even stop an expensive calculation, while a Promise
would still do the expensive calculation even when the receiver already decided to not not be interested in the result anymore and won't wait for it. –
Madelaine vendors: Observable<any> = this.http.get(url).map(res => res.json()).publishLast().refCount();
The cool thing about using Observable in this case is you can subscribe to it in multiple places and it won't requery. Anyway, just one reason to use Observables. –
Chaney Promise
, along with async
/await
makes your code flat again! In a majority of situations, and in projects that don't deal with rocket science, there is no need to write those horrible nested functions with unnecessarily complicated chains of methods. You can use async
/await
today with transpilers, like TypeScript
, and write actual human-readable flat code without any of the rxjs
boilerplate. You will probably still need rxjs
sometimes in select situations, because it really does have a lot of things to offer. –
Metamorphose Promise
or Observable
any one can be used. When stream of values being returned that's where Observable
shine and also with some addition features it has with retry()
, forEach
, calcelable etc. I am bit newbie to this, so can you please provide an example where an api would return stream of values? Also any example where api request need to be canceled? –
Grochow isLoggedIn
state. A component or service might want to get notified about changes. The component or service subscribes to an Observable and gets notified whenever the user logs in or out. Another example would be a progress bar component that gets passed an Observable the provides progress updates. –
Madelaine Both Promises
and Observables
provide us with abstractions that help us deal with the asynchronous nature of our applications. The difference between them was pointed out clearly by Günter and @Relu.
Since a code snippet is worth a thousand words, let’s go through the below example to understand them easier.
Thanks @Christoph Burgdorf for the awesome article
Angular uses Rx.js Observables instead of promises for dealing with HTTP.
Suppose that you are building a search function that should instantly show you results as you type. It sounds familiar, but there are a lot of challenges that come with that task.
The demo will simply consist of two files: app.ts
and wikipedia-service.ts
. In a real world scenario, we would most likely split things further up, though.
Below is a Promise-based implementation that doesn’t handle any of the described edge cases.
wikipedia-service.ts
import { Injectable } from '@angular/core';
import { URLSearchParams, Jsonp } from '@angular/http';
@Injectable()
export class WikipediaService {
constructor(private jsonp: Jsonp) {}
search (term: string) {
var search = new URLSearchParams()
search.set('action', 'opensearch');
search.set('search', term);
search.set('format', 'json');
return this.jsonp
.get('http://en.wikipedia.org/w/api.php?callback=JSONP_CALLBACK', { search })
.toPromise()
.then((response) => response.json()[1]);
}
}
We are injecting the Jsonp
service to make a GET request against the Wikipedia API with a given search term. Notice that we call toPromise
in order to get from an Observable<Response>
to a Promise<Response>
. Eventually end up with a Promise<Array<string>>
as the return type of our search method.
app.ts
// check the plnkr for the full list of imports
import {...} from '...';
@Component({
selector: 'my-app',
template: `
<div>
<h2>Wikipedia Search</h2>
<input #term type="text" (keyup)="search(term.value)">
<ul>
<li *ngFor="let item of items">{{item}}</li>
</ul>
</div>
`
})
export class AppComponent {
items: Array<string>;
constructor(private wikipediaService: WikipediaService) {}
search(term) {
this.wikipediaService.search(term)
.then(items => this.items = items);
}
}
There is not much of a surprise here either. We inject our WikipediaService
and expose its functionality via a search method to the template. The template simply binds to keyup and calls search(term.value)
.
We unwrap the result of the Promise that the search method of the WikipediaService returns and expose it as a simple array of strings to the template so that we can have *ngFor
loop through it and build up a list for us.
See the example of Promise-based implementation on Plunker
Where Observables really shine
Let’s change our code to not hammer the endpoint with every keystroke, but instead only send a request when the user stopped typing for 400 ms
To unveil such super powers, we first need to get an Observable<string>
that carries the search term that the user types in. Instead of manually binding to the keyup event, we can take advantage of Angular’s formControl
directive. To use this directive, we first need to import the ReactiveFormsModule
into our application module.
app.ts
import { NgModule } from '@angular/core';
import { BrowserModule } from '@angular/platform-browser';
import { JsonpModule } from '@angular/http';
import { ReactiveFormsModule } from '@angular/forms';
@NgModule({
imports: [BrowserModule, JsonpModule, ReactiveFormsModule]
declarations: [AppComponent],
bootstrap: [AppComponent]
})
export class AppModule {}
Once imported, we can use formControl from within our template and set it to the name "term".
<input type="text" [formControl]="term"/>
In our component, we create an instance of FormControl
from @angular/form
and expose it as a field under the name term on our component.
Behind the scenes, term automatically exposes an Observable<string>
as property valueChanges
that we can subscribe to. Now that we have an Observable<string>
, overcoming the user input is as easy as calling debounceTime(400)
on our Observable
. This will return a new Observable<string>
that will only emit a new value when there haven’t been coming new values for 400 ms.
export class App {
items: Array<string>;
term = new FormControl();
constructor(private wikipediaService: WikipediaService) {
this.term.valueChanges
.debounceTime(400) // wait for 400 ms pause in events
.distinctUntilChanged() // ignore if next search term is same as previous
.subscribe(term => this.wikipediaService.search(term).then(items => this.items = items));
}
}
It would be a waste of resources to send out another request for a search term that our application already shows the results for. All we have to do to achieve the desired behavior is to call the distinctUntilChanged
operator right after we called debounceTime(400)
See the example of Observable implementation on Plunker
For dealing with out-of-order responses, please check the full article http://blog.thoughtram.io/angular/2016/01/06/taking-advantage-of-observables-in-angular2.html
As far as I am using HTTP in Angular, I agree that in the normal use cases there is not much difference when using Observable over Promise. None of the advantages are really relevant here in practice. I hope I can see some advanced use case in the future :)
Learn more
valueChanges
trigger only when value is changed? so whats the usage of .distinctUntilChanged()
? –
Kanter Both Promises and Observables will help us work with the asynchronous functionalities in JavaScript. They are very similar in many cases, however, there are still some differences between the two , Promises are values that will resolve in asynchronous
ways like HTTP calls. On the other hand, Observables deal with a sequence of asynchronous events. The main differences between them are listed below:
Promise:
Observable:
Also, I've created the graphical image for you below to show the differences visually:
Promise
is the wrong way to think about how promises. The Promise
's responsibility it only to handle success or failure in an async compatible way.. If you want to cancel an http request you cancel the request, not the promise, and make the result of cancelling either fulfil or reject the Promise. jsfiddle.net/greggman/ea0yhd4p –
Diploblastic There is one downside of Observables missing in the answers. Promises allow to use the ES7 async/await functions. With them you can write asynchronous code like it would be a synchronous function call, so you don't need callbacks anymore. The only possibility for Observables to do this, is to convert them to Promises. But when you convert them to Promises, you can only have one return value again:
async function getData(){
const data = await observable.first().toPromise();
//do stuff with 'data' (no callback function needed)
}
Further reading: How can I `await` on an Rx Observable?
async/await
weren't commonly available when many of the top answers were written. Back then there Observable
s were Promise
s on steriods so there was a benefit to exclusively use Observable
s. Now using both is better because you don't end up in callback-hell. –
Gerlachovka Promises
Observables
Note: A list of operators along with their interactive diagrams is available here at **RxMarbles.com**
Promises and Observables both handle the asynchronous call only.
Here are the differences between them:
Observable
Promise
Emits only a single value at a time
Calls the services without .then and .catch
Cannot be canceled
Does not provide any operators
Let's say you want to go to the beach. You have to make a decision based on weather. You have three ways:
You look outside and see the raindrops, so you change your mind. This is a synchronous operation. You stopped what you were doing, went to check the outside, got the result and then got back to what you were doing.
You ask your brother who is next to you to check the weather conditions for today. While he is checking the weather, you still keep doing what you were doing. This is an async operation. You gave a task to your brother and waiting for the promise resolved. In this case, you are getting one response and after you get your response, you no longer get any update.
This time, you turn on a radio and listening to a weather channel that broadcasts weather conditions 24/7. In this scenario, instead of getting one single response, the response is ongoing. This response is like a subscription
to an observable
. the observable is the "weather" and the subscription is the "radio signals that keep you updated". As long as your radio is on, you are getting every available update. You are not missing any information until you turn off the radio. When you turn off the radio, it means "you unsubscribed".
I have summarized the differences below,
Observable:
function
that takes an observer
and returns a function Observer: an object with next, error.
subscribe/unsubscribe
to its data stream, emit
next value to the observer, notify
the observer about errors
and
inform the observer about the stream completion
function to handle next value
, errors and
end of stream (UI events, http responses, data with web sockets).multiple values
over timecancel-able/retry-able
and supports operators such as map, filter, reduce
, etc.Observable.create()
- returns Observable that can invoke methods on
-Observer Observable.from()
- converts an array or iterable into
-Observable Observable.fromEvent()
- converts an event into Observable
-Observable.fromPromise()
- converts a Promise into Observable
-Observable.range()
- returns a sequence of integers in the specified rangePromise:
A promise represents a task that will finish in the future;
Promises become resolved by a value
;
Promises get rejected by exceptions;
Not cancellable
and it returns a single value
A promise expose a function (then)
-then returns a new promise
;
-allows for the attachment
of that will be executed based on
state
;
-handlers
are guaranteed
to execute in order attached
;
I believe all the other answers should clear your doubts. Nevertheless, I just wanted to add that observables are based on functional programming, and I find very useful the functions that come with it like map, flatmap, reduce, zip. The consistency the web achieves especially when it depends on API requests is a brutal improvement.
I strongly recommend this documentation, since it's the official documentation of reactiveX and I find it to be the most clear out there.
If you want to get into observables, I would suggest this 3-part post: http://blog.danlew.net/2014/09/15/grokking-rxjava-part-1/
Although it's meant for RxJava, the concepts are the same, and it's really well explained. In reactiveX documentation, you have the equivalences for each function. You must look for RxJS.
You can always use an observable for dealing with asynchronous behaviour since an observable has the all functionality which a promise offers (+ extra). However, sometimes this extra functionality that Observables offer is not needed. Then it would be extra overhead to import a library for it to use them.
Use promises when you have a single async operation of which you want to process the result. For example:
var promise = new Promise((resolve, reject) => {
// do something once, possibly async
// code inside the Promise constructor callback is getting executed synchronously
if (/* everything turned out fine */) {
resolve("Stuff worked!");
}
else {
reject(Error("It broke"));
}
});
//after the promise is resolved or rejected we can call .then or .catch method on it
promise.then((val) => console.log(val)) // logs the resolve argument
.catch((val) => console.log(val)); // logs the reject argument
So a promise executes some code where it either resolves or rejects. If either resolve or reject is called the promise goes from a pending state to either a resolved or rejected state. When the promise state is resolved the then()
method is called. When the promise state is rejected, the catch()
method is called.
Use Observables when there is a stream (of data) over time which you need to be handled. A stream is a sequence of data elements which are being made available over time. Examples of streams are:
In the Observable itself is specified when the next event happened, when an error occurs, or when the Observable is completed. Then we can subscribe to this observable, which activates it and in this subscription, we can pass in 3 callbacks (don't always have to pass in all). One callback to be executed for success, one callback for error, and one callback for completion. For example:
const observable = Rx.Observable.create(observer => {
// create a single value and complete
observer.onNext(1);
observer.onCompleted();
});
source.subscribe(
x => console.log('onNext: %s', x), // success callback
e => console.log('onError: %s', e), // error callback
() => console.log('onCompleted') // completion callback
);
// first we log: onNext: 1
// then we log: onCompleted
When creating an observable it requires a callback function which supplies an observer as an argument. On this observer, you then can call onNext
, onCompleted
, onError
. Then when the Observable is subscribed to it will call the corresponding callbacks passed into the subscription.
I've just dealt with an issue where Promises were the best solution, and I'm sharing it here for anyone stumbling across this question in the event it's useful (this was exactly the answer I was looking for earlier):
In an Angular2 project I have a service that takes some parameters and returns a value list to populate drop down menus on a form. When the form component initializes, I need to call the same service multiple times with different parameters to define a number of different dropdown menus, however if I simply queue up all the variables to call the service, only the last one succeeds and the rest error out. The service fetching from the database could only handle one request at a time.
The only way to successfully populate all the dropdown menu variables was to call the service in a way that prevented a new request from being processed until the last request was finished, and the Promise / .then mechanism solved the problem nicely.
fetchValueList(listCode): Promise<any> {
return this.dataSvc.getValueList(listCode, this.stateSvc.currentContext, this.stateSvc.currentLanguageCode)
.map(response => response.json())
.toPromise();
}
initializeDropDowns() {
this.fetchValueList('First-Val-List')
.then(data => {
this.firstValList = data;
return this.fetchValueList('Second-Val-List')
}).then(data => {
this.secondValList = data;
return this.fetchValueList('Third-Val-List')
}).then(data => {
this.thirdValList = data;
}) }
I defined the functions in the component, and then called initializeDropDowns() in ngOnInit.
The fetchValueList function returns a Promise, so the first call passes the first listCode and when the Promise resolves, the return value is in the data variable in the .then block where we can assign it to the this.firstValList variable. As the function has returned data, we know the service has finished and it's safe to call again with the second listCode, the return value is in the data variable in the next .then block and we assign it to the this.secondValList variable.
We can chain this as many times as required to populate all the variables, and on the last code block we simply omit the return statement and the block terminates.
This is a very specific use case where we have a single service that needs to be called multiple times as the component initializes, and where the service has to complete its fetch and return a value before it can be called again, but in this case, the Promise / .then method was ideal.
scan()
to build a stream of sequential observables. However, your approach is maybe more explicit and easier to understand. –
Sidewalk async
/await
? async initializeDropDowns() { this.firstValList = await this.fetchValueList('First-Val-List'); this.secondValList = await this.fetchValueList('Second-Val-List'); this.thirdValList = await this.fetchValueList('Third-Val-List'); }
–
Inflatable async
/await
here is a great example of Promises being cleaner and easier to read that Observables. Though I would really look into why that backend service cannot handle multiple concurrent calls. What about when a user has two tabs open to your page? Or if you have two users... –
Inflatable Both are used to handle async code.
Please look for the promise example. The promise constructor passes a resolve reference function which will get called when it gets called with some value upon completion of some async task.
const promise = new Promise(resolve => {
setTimeout(() => {
resolve("Hello from a Promise!");
}, 2000);
});
promise.then(value => console.log(value));
Observable example now. Here we also pass a function to observable - an observer to handle the async task. Unlike resolve in the promise, it has the following method and subscribes in place of then.
So both handles async tasks. Now let's see the difference.
const observable = new Observable(observer => {
setTimeout(() => {
observer.next('Hello from a Observable!');
}, 2000);
});
observable.subscribe(value => console.log(value));
Promise
Observable
ability to emit multiple asynchronous values.
Used to handle the stream of events or values. Consider you have an array of numerous tasks or values, and you want every time value is inserted into this it should be handled automatically. Anytime you push a value into this array, all of its subscribers will receive the latest value automatically.
Observables are useful for observing input changes, repeated interval, broadcast values to all child components, web socket push notifications, etc.
Can be cancelled using unsubscribe method anytime.
One more last good part that promise that has is support for rxjs operators. You have many pipe operators majorly map, filter, switchMap, combineLatest, etc. to transform observable data before subscribing.
Promise - Provides a single future value. Not lazy. Not cancel-able. It will either reject or resolve.
Observable - Provides multiple future values. Lazy. Cancel-able. It provides other methods, like map, filter, and reduce.
Promise emits a single value while Observable emits multiple values. So, while handling a HTTP request, Promise can manage a single response for the same request, but what if there are multiple responses to the same request, then we have to use Observable. Yes, Observable can handle multiple responses for the same request.
Promise
const promise = new Promise((data) =>
{ data(1);
data(2);
data(3); })
.then(element => console.log(‘Promise ‘ + element));
Output
Promise 1
Observable
const observable = new Observable((data) => {
data.next(1);
data.next(2);
data.next(3);
}).subscribe(element => console.log('Observable ' + element));
Output
Observable 1
Observable 2
Observable 3
Short answer:
Observable is better. It has all Promises features plus extra features.
Long answer:
Promises:
Observables:
A Promise emits a single event when an async activity finishes or fails.
An Observable is like a Stream (in many languages) and permits to pass at least zero or more events where the callback is required for every event.
Frequently Observable is preferred over Promise since it gives the highlights of Promise and more. With Observable it doesn't matter if you need to handle 0, 1, or various events. You can use the similar API for each case.
Promise: promise emits a single value
For example:
const numberPromise = new Promise((resolve) => {
resolve(5);
resolve(10);
});
numberPromise.then(value => console.log(value));
// still prints only 5
Observable: Emits multiple values over a period of time
For example:
const numberObservable = new Observable((observer) => {
observer.next(5);
observer.next(10);
});
numberObservable.subscribe(value => console.log(value));
// prints 5 and 10
we can think of an observable like a stream which emits multiple values over a period of time and the same callback function is called for each item emitted so with an observable we can use the same API to handled asynchronous data. whether that data is transmitted as a single value or multiple values over some stretch of time.
Promise:
Observable:
Something I ran into that wasn't apparent from a first reading of the tutorial and documentation was the idea of multicasting.
Make sure you're aware that by default, multiple subscriptions will trigger multiple executions in an Observable. Multiple subscriptions to a single HTTP call Observable will trigger multiple identical HTTP calls unless you .share()
(enable multicasting).
A promise forces you to deal with one thing at a time, unwrap its data, handle exceptions, has language support for cool things like async/await, and is pretty bare-bones otherwise.
An Observable has lots of bells and whistles, but you need to understand the power you're working with or it can be misused.
Promise:
An Async Event Handler - The Promise object represents the eventual completion (or failure) of an asynchronous operation, and its resulting value.
Syntax: new Promise(executor);
For example:
var promise_eg = new Promise(function(resolve, reject) {
setTimeout(function() {
resolve('foo');
}, 300);
});
promise_eg.then(function(value) {
console.log(value);
// expected output: "foo"
});
console.log(promise_eg);
About Promise:
It has one pipeline, so it will return values only once when it’s called. It’s a one-way handler, so once called you may not able to cancel. Useful syntax you can play around, when() and then().
Observables:
Observables are lazy collections of multiple values over time. It’s really a great approach for async operations. It can be done with rxjs which has cross-platform support, can be used with Angular/React, etc.
It acts like stream liner and can be multi pipeline. So once defined, you can subscribe to get return results in many places.
Syntax: import * as Rx from "@reactivex/rxjs";
to init:
Rx.Observable.fromEvent(button, "click"),
Rx.Subject()
etc.
To subscribe: RxLogger.getInstance();
For example:
import { range } from 'rxjs';
import { map, filter } from 'rxjs/operators';
range(1, 200).pipe(
filter(x => x % 2 === 1),
map(x => x + x)
).subscribe(x => console.log(x));
Since it supports multi pipeline, you can subscribe to the result in a different location,
It has many more possibilities than promises.
Usage:
It has more possibilities, like map, filter, pipe, map, concatMap, etc.
Both Promises and Observables help us dealing with asynchronous operations. They can call certain callbacks when these asynchronous operations are done.
Angular uses Observables which is from RxJS instead of promises for dealing with HTTP
Below are some important differences in promises & Observables.
Below are some important differences in promises & Observables.
Promise
Observable
For better understanding refer to the https://stackblitz.com/edit/observable-vs-promises
I see a lot of people using the argument that Observable are "cancellable" but it is rather trivial to make Promise "cancellable"
function cancellablePromise(body) {
let resolve, reject;
const promise = new Promise((res, rej) => {
resolve = res; reject = rej;
body(resolve, reject)
})
promise.resolve = resolve;
promise.reject = reject;
return promise
}
// Example 1: Reject a promise prematurely
const p1 = cancellablePromise((resolve, reject) => {
setTimeout(() => resolve('10', 100))
})
p1.then(value => alert(value)).catch(err => console.error(err))
p1.reject(new Error('denied')) // expect an error in the console
// Example: Resolve a promise prematurely
const p2 = cancellablePromise((resolve, reject) => {
setTimeout(() => resolve('blop'), 100)
})
p2.then(value => alert(value)).catch(err => console.error(err))
p2.resolve(200) // expect an alert with 200
Promise: is an ES6 feature that deals with async code which executes immediately on the creation that can only emit a single value at the time and are not cancelable. With modern applications and the complexity of the functionality needs, it will be necessary to implement complex code, if in the case we are dealing to execute many promises at the same time, or filter before execution or make some transformation:
myPromise.then((resolvedValue) => {
console.log(resolvedValue);
}, (error) => {
console.log(error);
});
Observable: is an Object provided by the Rxjs library that helps us to work with reactive programming in JavaScript applications, which provides chaining and subscription to handle complex applications having the advantage to be cancelable, providing many values at the same time. In addition, we can benefit from applying chaining of other operators like retry()
, map()
, filter()
, switchMap()
, etc. which helps to deal with complex use cases and heavy user interfaces.
Example instant search:
search(terms: Observable<string>) {
return terms.pipe(
debounceTime(400),
distinctUntilChanged(),
switchMap((term) => this.searchEntries(term))
);
}
Example many APIS calls in parallel:
let character = this.http.get('https://jsonplaceholder.typicode.com/todos');
let characterHomeworld = this.http.get(
'https://jsonplaceholder.typicode.com/posts'
);
forkJoin([character, characterHomeworld]).subscribe((results) => {
console.log('result °', results[0]);
console.log('result 1', results[1]);
});
One more difference: Global vs. Imported
Promise is a standard built-in object, and you can use it directly. Check the browser support here.
const myPromise = new Promise((resolve, reject) => {
setTimeout(() => {
resolve('ready with out any installation');
}, 300);
});
myPromise
.then(value => { console.log(value) })
.catch(err => { console.log(err) });
Observable, Reactive Extensions for JavaScript need RxJS installation & import before use
import { Observable } from 'rxjs';
While the Günter Zöchbauer's answer is good in general, I don't think it emphasises that when dealing with Angular Components you almost always want to use an Observable because it supports cancellation. Promises cannot be cancelled and will resolve even if your component is destroyed. Angular tends to be forgiving until it's not.
For example, any manual change detection on a destroyed component will cause an exception:
ngOnInit() {
// Promise API
this.service.getData().then(d => {
this.data = d;
this.changeDetectorRef.detectChanges();
});
// Observable API
this.service.getData().pipe(takeUntil(this.unsubscribe)).subscribe((d) => {
this.data = d;
this.changeDetectorRef.detectChanges();
});
}
If your component is destroyed before the promise is resolved, you'll get an attempt to use destroyed view
error when the promise is resolved.
Alternatively, if you use observables with the takeUntil pattern, then as soon as your component is destroyed the subscription will be cancelled.
This is a bit of a contrived example, but executing code for a component that is destroyed is probably going to lead to bugs.
Promises are focused only for single values or resolves. Observables are stream of data.
Observables can be canceled, but promises can't be canceled.
The least known one, at least to me is:
If you want to read about it in detail, I wrote a blog post following on this answer - The 4 differences between Observables and Promises in JavaScript
A Promise is always asynchronous, while an Observable can be either synchronous or asynchronous. Observables return data many times as data change. Promise emits a single value. Observables are lazy collections of multiple values over time. It’s really a great approach for async operations. Promises are not lazy. Observables can be cancelled using unsubscribe(). Promises cannot be cancelled.
A Promise handles a single event when an async operation completes or fails.
Promises are auto-executed in the application, while observables are lazy. So we have to subscribe to observables to return the data.
We cannot unsubscribe promises. They will execute every time in contrast to Observables that can be unsubscribed.
I like to criticize using Observables
for API calls
:
I don't want to come across as overly critical, but I've had some lingering questions about the use of Observables
in HTTP/API calls
in Angular
, React
etc. While I like Observables
and appreciate their flexibility, I wonder whether they are always the right choice, especially when it comes to plain HTTP calls
?
You may notice that Promises
are suitable only for one-time calls
, while Observables
can keep streams open indefinitely. I understand the value of open-ended streams, but in my experience, one-time HTTP calls
make up a significant majority of use cases in most business applications. As it stands, If I have to use Angular
I often find myself using Observables
in a way that essentially mimics Promises
.
I see the potential for Observables
in handling event streams, such as click events and data streams, and I acknowledge their value in these contexts. However, when it comes to HTTP/API calls
it feels like we're trying to fit the 90% use case into the 10% use case with this approach.
I'm not against exploring alternative technologies, but I wonder if the shift toward Observables
is worth the complexity of it! particularly when it comes to HTTP calls. Perhaps it would make more sense to offer a lean, Promise-based HTTP
solution for the majority of use cases while encouraging those with genuine streaming needs to use RxJs
directly.
From my perspective, Promises
are ideal for simple, one-time asynchronous operations
that resolve to a single value. Observables
, in contrast, seem to be engineered for handling intricate data streams, with features like lazy evaluation, cancellation, and advanced transformation and composition of emitted values.
In conclusion, my view is that the choice between Promises
and Observables
for HTTP API calls
would be Promises
90 percent of the time.
UPDATE: Swagger/OpenAPI Client Code Generators
Due to the intrinsic nature of Promises
, it's easy to retrieve responses from async
functions with the await
keyword. Considering the fact that not everything or every feature in JavaScript
is Observable-friendly
, this can raise serious problems with features that are already adapted with Promises
but not with Observables
.
for example, there are great tools like openapi-generator that work very well with generators that use Promises
. The most important thing in working swagger/openAPI client code generators
is using access_token
in a way that to be injected into the generated codes, and getting access_token
in a secure way (not in browser storage! Please don't do that!) from the server session is most of the time Promise
like. you can always find a way to adapt a getAccessToken function that returns Promise to the generated codes, but with generated/created Observable codes it's painful.
Observables and Promises are helping us to work with the asynchronous functionalities in JavaScript/TypeScript. They are very similar in many cases, however, there are still some differences between them.
Promise and Observable are both used in Angular(typescript) to handle asynchronous data, but they differ in their usage and their behavior.
expample of uses cases : Error handling and recovery/Handling sequential operations/Inter-component communication
import { HttpClient } from '@angular/common/http';
constructor(private http: HttpClient) {}
fetchData() {
return new Promise((resolve, reject) => {
this.http.get('https://api.examplepromise.com/data')
.toPromise()
.then(data => resolve(data))
.catch(error => reject(error));
});
}
Blockquote
import { fromEvent } from 'rxjs';
constructor(private elementRef: ElementRef) {}
listenToButtonClicks() {
fromEvent(this.elementRef.nativeElement, 'click')
.subscribe(event => console.log(event));
}
Differences between Promise
and Observable
Promise | Observable | |
---|---|---|
How many events can receive | Exactly 1 event or error, which also means that 'stream' completed and there won't be new events or errors | 0 or more events, 0 or 1 error and 1 completion event, which we can listen and react to even if there weren't events or errors |
Completion | Happens once after first event or error | Happens once after error(err) or complete() is called, firing regular event via next(event) doesn't complete Observable |
Cancellation support | Doesn't support running task cancellation, can't cancel requests in progress represented with pure Promise |
Supports running task cancellation, can cancel requests in progress represented by Observable |
When starts listening events | Immediately after Promise is created |
After subscribe is called, we are not required to create Observable only when we are ready to start listening event |
Does store last values? | Always stores 1 last value/error | Depending on implementation and operators used. Most Observable -s don't store last values, but we can create observable which store n last values via operators like shareReplay(1) |
Has analogues in other languages? | No, Promise -s in other languages have differences, like Task in C# has cancellation support |
Yes, like https://github.com/dotnet/reactive in C#, or https://github.com/ReactiveX/RxJava in Java |
Supports easy async/await syntax? | Yes | No |
Supports specifying schedulers on which to do computations? | No, but it is not that difficult to wrap calls which notify Promise in schedule |
Provides scheduler support via operators or overloads https://rxjs.dev/guide/scheduler#scheduler-types |
After going through all the answers to this question, there's a risk that readers may conclude Observables are superior to Promises, and thus, should be used in every case, even when dealing with single-value returns. The assumption that Observables have rendered Promises obsolete, and therefore, should be the rule of thumb, can often lead to convoluted code, or what I call 'Trash code'. I will try to dispel such misconceptions and complement Gunter's insightful answer.
The Angular Team's recommendation to favor Observables over Promises in their documentation has, unfortunately, caused considerable misinterpretation, leading to severe disarray in the realm of front-end web applications.
Consider a small front-end web application. Here, the observable, sourced from an HTTP get, is utilized directly in your template with a |async
operator. It's straightforward, and the subscribe action is managed by the |async
operator. However, in this instance, there's no data transformation or call for other async functions. While this scenario is common in demo apps, it's rarely the case in large front-end web applications.
Observables from the RxJS library are tailored for handling streams and events, not single-value asynchronous operations. Overusing Observables for all async operations introduces unnecessary complexity, cluttering your code with stream operators like map, filter, reduce, switchMap, mergeMap, and concatMap when they are entirely irrelevant.
Conversely, Promises, since Typescript 1.7, are compatible with async/await operators (even before Angular 2). With Promises, we can eliminate callback chains and nesting, thereby evading the notorious 'pyramid of doom'.
Observable-returning functions' signatures do not provide any indication as to whether they will return a single or multiple events. However, when a Promise is used for an async single-value operation, the return type assures that it will yield one single value, or an error. Usage of a correctly typed function is more predictable, and prevents the need to add documentation to specify the operation will return one single value.
This Angular documentation article demonstrates how Observables are akin to arrays. Now, picture a scenario where you decide to replace single values with arrays throughout your app: Array<bool>
instead of bool
, Array<string>
instead of string
, etc. This replacement eradicates the simple if
usage, and you must now resort to filter
. To modify an object, you'll need the map
operator. How about adding two numbers? Well, you use the lodash zipWith
operator off course:
let quantity1 = [1];
let quantity2 = [2];
let total = zipWith(quantity1, quantity2, (a, b) => a + b);
console.log(total); // [3]
Such a scenario may seem absurd, but well documented in Angular and some youtube videos, some developers would be ok with it and say "it is the way", "Arrays are better because it can handle multiple value of different types with polymorphism".
In a strongly typed world, maintaining strict types for objects leads to code that's more understandable and maintainable.
Consider a service function that calls multiple APIs:
/**
* Searching for the localization of a client file
*
* @remark This function does not comply with the specification of the ILocalizationSearchService interface
* as no errors are raised in case of an exception.
*/
public abstract getFileLocalization( countryName: string, clientName: string, fileNumber: number, fileName: string ): Observable<string>;
To get the file localization, the Angular Service has to sequentially call external APIs. Localization is obtained from a folder, which comes from a box, which is derived from the client, which is ultimately obtained from the country.
When using Observable versions, you have two options: you can use the subscribe function, which is fundamental in the Observer pattern:
or use RxJS operators that handle the subscribe function and exception propagation for you:
The subscribe version leads to a pyramid of doom due to the number of nested callbacks. An alternative is to use RxJS operator version, which requires the use of the 'switchMap' operator. But the question arises, why 'switchMap' and not 'concatMap' or 'mergeMap'? The differences between these operators only matter when dealing with streams. So isn't it preferable to eliminate this question by using async/await operators? Here's the Promise version:
async getFileLocalization(countryName: string, clientName: string, boxNumber: number, folderName: string): Promise<string> {
// Searching for the country
let country = await this.proSer.FindCountry(countryName);
// Getting the client
let client = await this.proSer.GetClient(country, clientName);
// Searching for the box
let box = await this.proSer.FindBox(client, boxNumber);
// Searching for the file
let folder = await this.proSer.FindFile(box, folderName);
// Getting localization
return await this.proSer.GetLocalisation(folder);
}
The complete code for this can be found on Stackblitz: https://stackblitz.com/edit/typescript-53hmru
Cancellation with Promises
Promises in Angular Apps may try to access properties of a component or an instance that may have been destroyed in the meantime. This can lead to memory leaks if the component is destroyed. There are two common ways to handle this:
Use a cancellation token (like in C#): pass a cancellation token as a parameter to the promise. If the instance is prone to destruction, trigger a rejection of the promise, or throw an exception with an async function.
async getStuff( stuffId: number, cancellation: CancellationToken ) {
const stuff = await this.stuffService.getStuff( stuffId );
if( cancellation.IsCancelled )
throw 'cancelled';
this.stuff = stuff;
}
ngOnDestroy() {
this.cancellation.cancel();
}
Transform your async function/promise to an observable using from. The observable will be used solely for unsubscribing, with the only streaming operator used being 'takeUntil'.
getStuff( stuffId: number ) {
from( this.stuffService.getStuff( stuffId ) ).pipe(takeUntil(this.destroy$)).subscribe(stuff => {
this.stuff = stuff;
});
}
ngOnDestroy() {
this.destroy$.next();
this.destroy$.complete();
}
Observables are best suited for processing streams of events such as user actions, peripheral events (like a Bluetooth connection or GPS positioning), or server events (like server-sent events or WebSocket pushes).
In summary, understanding when to use Observables and Promises is crucial. Replacing Promises with Observables may lead to unnecessary complexity and reduce maintainability
There are lots of answers on this topic already so I wouldn't add a redundant one.
But to someone who just started learning Observable / Angular and wonders which one to use compare with Promise, I would recommend you keep everything Observable and convert all existing Promises in your project to Observable.
Simply because the Angular framework itself and its community are all using Observable. So it would be beneficial when you integrate framework services or third-party modules and chaining everything together.
Of course, no opinion is 100% correct in all cases, but at least I think 98% of the time for regular commercial projects implemented in Angular framework, Observable is the right way to go.
Even if you don't like it at the starting point of your simple hobby project, you'll soon realise almost all components you interact with in Angular, and most of the Angular-friendly third-party frameworks are using Observables, and then you'll end up constantly converting your Promise to Observable in order to communicate with them.
Those components includes, but are not limited to: HttpClient, Form builder, Angular material modules/dialogs, Ngrx store/effects and ngx-bootstrap.
In fact, the only Promise from the Angular ecosystem I dealt with in the past two years was APP_INITIALIZER
.
© 2022 - 2024 — McMap. All rights reserved.