Recently I've been moved to a new project which uses angular 6 as frontend framework and spring for REST services.
The project is in development for 2 years now and what I've observed was that almost all HTTP request made with angular HttpClient is then piped to take filter from rxjs. All REST APIs emit only one value. There's no need for manual cancellation and the laziness property of observables.
My intuition is that using toPromise() would be a better way to code.
What are your thoughts?
//customer-service.ts
constructor(private http: HttpClient) {
}
public getCustomers() {
return http.get('/customers');
}
//component.ts
public ngOnInit() {
this.customerService.getCustomers().pipe(take(1)).subscribe((customers) => {
//do some stuff
})
}
My proposed approach:
//customer-service.ts
constructor(private http: HttpClient) {
}
public getCustomers() : Promise<Array<Customer>> {
return http.get('/customers').toPromise();
}
//component.ts
public ngOnInit() {
this.customerService.getCustomers().then((customers: Array<Customer>) => {
//do some stuff
})
}
I think that my approach is better because it is strongly typed and it's cleaner.
Actually the accepted answer is misleading by saying that transforming an observable to a promise is like taking a step backward. Rather it's about what your needs are.
as stated in Angular Documentation:
Observables differentiate between chaining and subscription. Promises only have .then() clauses. This makes observables useful for creating complex transformation recipes to be used by other part of the system, without causing the work to be executed.
And the difference between toPromise() and take(1) (or for short use operator first()), is that take(1) completes after the 1st value emitted, while toPromise waits for the last emitted value (waits for the observable to complete then resolves the last value emitted).
Another difference between observables and promises that might interest you, is that Observable subscriptions are cancellable while promises are not. As a concrete example, say your API /customers takes time but you dont need the results anymore and you navigate to another page, unsubscribing cancels the HTTP request.
As you mentioned strongly typed we can do the same in observable approach.
public getCustomers():Observable<Array<Customer>> {
return http.get<Array<Customer>>('/customers');
}
//component.ts
public ngOnInit() {
this.customerService.getCustomers()
.pipe(take(1))
.subscribe((customers: Array<Customer>) => {
//do some stuff
});
}
For HttpRequest with single HttpResponse you may rely on promise
approach.
But HttpRequest with multiple response such as progress/stream of
data(blob) go for the Observable approach.
Try to use Observable as much as possible, once you familiar with you wont go for other options. So Give it a try!
I recommend to keep using the Observable way.
Why? Because later it will be easier to:
build upcoming logic (like mapping, filtering, etc.) for planned features
use the code, due mixing the two approach makes it harder to read/write
On the other hand you have right, your code is more readable as the code from others, but you can also change the Observable to be as good as yours (just add strong typing to it), and it still keeps the functionality and consistency of the Observables.
Going from observables to promises is a step back.
It's like going from a Porsche 911 to a Fiat multipla.
So no, you shouldn't use toPromise(), and no, your way isn't "better" (that's some ego there buddy !)
I think that my approach is better because it is strongly typed and it's cleaner.
Typing an HTTP answer isn't depending on pormises or observables, but on the developer himself. And cleaner is a matter of perspective, personally I hate seeing toPromise()s.
And the major drawback of your solution is that once converted to a promise, you can't pipe anything anymore, making your functions less versatile.
But their code isn't the best either. Usually this kind of behavior is used for stores and caches, are you sure you didn't omit something ?
Anyway, if not, and I only rely on the code provided, this would be the right code :
public getCustomers() {
return http.get<Customer[]>('/customers');
}
....
public ngOnInit() {
this.customerService.getCustomers()
.subscribe((customers) => {...})
}
Related
How does one decide between promises, async awaits, and mapping operators like concatMap?
Here's my specific case, but I'm also curious about how you decide in general:
I am making an http call to my backend, and then I make another http call afterwards. When processing the json data from the second call, I need to use values that are returned by the first call. In this situation, is it better to use async await, a promise or concatMap? Also in general, what are the guidelines for deciding which to use?
Here is what I current have, using concatMap. (I am dynamically generating child components from my getTask http call, and each child component needs to have access to annotationFormats).
this.dashboardService.getAnnotationFormats()
.pipe(
concatMap(annotationFormats=> this.dashboardService.getTasks())
)
.subscribe(
(tasks)=>{
for(let task of tasks){
const componentFactory=this.CFR.resolveComponentFactory(DashboardItemComponent);
const componentRef=this.vc.createComponent(componentFactory);
componentRef.instance.task=task;
componentRef.instance.annotationFormats=annotationFormats;
componentRef.instance.compInteraction=this;
this.taskRef.push(componentRef);
}
}
);
Async/await and promises are basically the same with different syntax. Asynchronous code that will run once after some job has been finished.
As a rule, I would never never use none of those while using Angular. Angular comes with RxJS out of the box, which is so much more than promises. You can use RxJS for running async code once when a job has completed, but it also gives you the possibility of creating streams of data and manipulating them in so many different ways.
It does take a bit to fully understand RxJS and reactive programming but once you do you realize how much you can do with it.
In your case, I like to use the operator forkJoin, since the two requests seem independent from each other. You can give it a list of resources you want to obtain and will execute the async code in subscribe once they have all completed, which makes it perfect for http requests:
forkJoin({
annotationFormats: this.dashboardService.getAnnotationFormats(),
tasks: this.dashboardService.getTasks(),
})
.subscribe(
({tasks, annotationFormats})=>{
for(let task of tasks){
const componentFactory=this.CFR.resolveComponentFactory(DashboardItemComponent);
const componentRef=this.vc.createComponent(componentFactory);
componentRef.instance.task=task;
componentRef.instance.annotationFormats=annotationFormats;
componentRef.instance.compInteraction=this;
this.taskRef.push(componentRef);
}
}
);
Take your time to learn RxJS, I guarantee it will pay off. Whenever you are using RxJS and it feels too complex or wrong, that is because it probably is. Head to the RxJS documentation and look for something that might useful, and if you don't find anything a quick google search will probably get you the solution anyways. Point is, don't just use it blindly, always try to understand how it works.
I hope this is useful. :)
Edit:
For RxJS < 6.5, the syntax is a bit different:
forkJoin(
this.dashboardService.getTasks(),
this.dashboardService.getAnnotationFormats()
)
.subscribe(
([tasks, annotationFormats])=>{
for(let task of tasks){
const componentFactory=this.CFR.resolveComponentFactory(DashboardItemComponent);
const componentRef=this.vc.createComponent(componentFactory);
componentRef.instance.task=task;
componentRef.instance.annotationFormats=annotationFormats;
componentRef.instance.compInteraction=this;
this.taskRef.push(componentRef);
}
}
);
Notice we pass the resources as arguments, not as an object, and the result in the subscribe will be in an array form instead of an object too.
They have different use. async/await is used when you want to hold on a place where you have written some asynchronous code. while primises are tool to spot a place where async code is executed and invokes callback.
Use case: Call a function every minute (60000 ms) that dispatches store action to fetch lastUpdated status of items, which upon response and filtering, updates the store, and updated store is read as an observable and displayed in the view). This needs to happen for as long as the web app is open (so indefinitely).
Currently, I'm using this:
this.refreshDate = window.setInterval(
() => this.store.dispatch(new FetchLastUpdate())
, 60000);
And when view is destroyed/dismounted, I delete the interval as so:
if (this.refreshDate) {
clearInterval(this.refreshDate);
}
Is this efficient/effective, or is it troublesome?
Why would I want to use an RxJS polling strategy like:
interval(60000)
.pipe(
startWith(0),
switchMap(() => this.store.dispatch(new FetchLastUpdate()))
);
Or
timer(0, 60000)
.pipe(
switchMap(() => this.store.dispatch(new FetchLastUpdate()))
);
TL;DR: window.setInterval() vs. RxJS timer()/interval()
Conclusion/answers (for ease of research):
There is great benefit to using RxJS functions to set an interval or perform polling, these benefits are explained in the selected answer but also in comments, but it is concluded (by discussions in the comments) that for the very simple requirement defined in the "Use case" section at the beginning of this post, it is unnecessary to use RxJS, and in fact if you are not using RxJS in any other part of your program, do not import it just for this, however in my case, I had already imported and used RxJS elsewhere.
Advantage of RxJS:
Laziness
You can create your Observables and until you call subscribe nothing is happening. Observable = pure function. This gives you more control, easier reasoning and allows for next point...
Composability
You can combine interval/timer with other operators creating custom logic very easily in unified way - for example you can map, repeat, retry, take... etc. see all operators
Error Handling
In case of an error you are responsible for calling clearTimeout/clearInterval - Observables are handling this for you. Resulting in cleaner code and fewer memory leak bugs.
Of course anything you do with Observables you can also do without Observables - but that's not the point. Observables are here to make your life easier.
Also note that interval/timer are not good observable factories for polling because they do not "wait" for your async action to finish (you can end up with multiple async calls running over each other). For that I tend to use defer and repeatWhen like this:
defer(() => doAsyncAction())
.pipe(
repeatWhen(notifications => notifications.pipe(delay(1234)))
);
window.setInterval doesn't care about your callbacks state, it'll execute at the given interval despite the status of the execution of the past callback, and the only way to make it stop and skip is clear the interval or reinitialize it.
On the other hand, RxJS Observable based solutions(interval, timer) allow you to pipe conditional operators (takeWhile, skipWhile for example) which allows you to add a stop or implement a stop-start logic by just flipping a boolean flag, instead of adding complicated logic of clearing the interval, and then recreating it.
And they are observables, you can listen to them all across the application, and attach any number of listeners to it.
Error Handling is better too, you subscribe to all successes, and handle everything in a catch callback.
I am using Rxjs Observables to handle nested ajax request like the following way:
Rx.Observable.fromPromise($.getJSON('list1.json'))
.switchMap(function responseA(aResponse){
/* processing aResponse*/
if(aResponse.fileName){
return Rx.Observable.fromPromise($.getJSON(aResponse.fileName));
}
return Rx.Observable.fromPromise($.getJSON('list2.json'));
})
.subscribe(function(finalResponse){
/* processing finalResponse */
});
But, as you know, it can also be done without using Observables and with only promises:
$.getJSON('list1.json')
.then(function responseA(aResponse){
/* processing aResponse*/
if(aResponse.fileName){
return $.getJSON(aResponse.fileName);
}
return $.getJSON('list2.json');
})
.then(function(finalResponse){
/* processing finalResponse */
});
Both code works, but it seems to me that it is more clean in terms of code to use promises.
Am I missing something here as I've heart that Rx Observable is more standard and efficient to handle asynchronous requests.
Which one (promise or Observable) will be the best in terms of code organization, convention and performances to handle ajax request ?
If I prefer to use Observable then which operators (switchMap/MergeMap) will be preferable in these kind of situation ?
Am I missing something here as I've heart that Rx Observable is more standard and efficient to handle asynchronous requests.
No, you're not missing anything. Rx is really useful but in that particular case promise based code is simpler.
In general, if you need a singular value - prefer a promise. If you need multiple values in/out - use observables (or the further ahead in spec async iterators).
Rx would be nice if you want to quickly need to add:
Retrying the requests if they failed (with Observable.defer).
Only caring about the last request.
If you need built in cancellation.
It's worth mentioning - promises can do all that if you use a library. It's not a fundamental property of observables.
Rx really shines if your input is more than a single call. If you needed to make these calls whenever the user clicks something, ignore clicks on some condition, debounce it to 100ms and then only care about the last call - Rx would be really useful.
In this particular case - promises are simpler and fine. Your promise code can be simplified further:
$.getJSON('list1.json').then(x => $.getJSON(x.fileName || 'list2.json'))
There are couple of really good articles out there in web, here is couple of them, I hope those will help you to understand the difference properly.
What Promises Do That Observables Can’t
https://egghead.io/lessons/rxjs-rxjs-observables-vs-promises
Is there a way to have a hot observable from an EventEmitter (or equivalent available in Angular 2 alpha 46 / RxJS 5 alpha)? i.e. if we subscribe after the value is resolved, it triggers with the previously resolved value. Similar to what we have when always returning the same promise.
Ideally, only using Angular 2 objects (I read somewhere a light RxJS would be embedded later to remove the dependency), otherwise importing RxJS is fine. AsyncSubject seems to match my need, but it is not available in RxJS 5 alpha.
I tried the following, without success (never triggers). Any idea about how to use it?
let emitter = new EventEmitter<MyObj>();
setTimeout(() => {emitter.next(new MyObj());});
this.observable = emitter;
return this.observable.share();
Full plunker here comparing hot and cold
Usecase: reach some async objects only once (for example a series of HTTP calls merged/wrapped in a new EventEmitter), but provide the resolved async object to any service/component subscribing to it, even if they subscribe after it is resolved (the HTTP responses are received).
EDIT: the question is not about how to merge HTTP responses, but how to get a (hot?) observable from EventEmitter or any equivalent available with Angular 2 alpha 46 / RxJS 5 alpha that allows to subscribe after the async result is retrieved/resolved (HTTP is just an example of async origin). myEventEmitter.share() does not work (cf plunker above), although it works with the Observable returned by HTTP (cf plunker from #Eric Martinez). And as of Angular 2 alpha 46, .toRx() method does not exist any more, the EventEmitter is the observable and subject itself.
This is something working well with promises as long as we always return the same promise object. Since we have observers introduced with HTTP Angular 2 services, I would like to avoid mixing promises and observers (and observers are said to be more powerful than promises, so it should allow to do what is easy with promises).
Specs about share() (I haven't found doc for version 5 alpha - version used by Angular 2) - working on the Observable returned by the Angular 2 HTTP service, not working on EventEmitter.
EDIT: clarified why not using the Observable returned by HTTP and added that not using RxJS directly would be even better.
EDIT: changed description: the concern is about multiple subscriptions, not merging the HTTP results.
Thanks!
The functionality you seem to be describing is not that of a cold observable but more than of a Rx.BehaviourSubject. Have a look here for an explanation on Rxjs subjects : https://github.com/Reactive-Extensions/RxJS/blob/master/doc/gettingstarted/subjects.md.
I quote from there :
BehaviourSubject is similar to ReplaySubject, except that it only stored the last value it published. BehaviourSubject also requires a default value upon initialization. This value is sent to observers when no other value has been received by the subject yet. This means that all subscribers will receive a value instantly on subscribe, unless the Subject has already completed.
The Rx.AsyncSubject would be the closest in behaviour to a promise :
AsyncSubject is similar to the Replay and Behavior subjects, however it will only store the last value, and only publish it when the sequence is completed. You can use the AsyncSubject type for situations when the source observable is hot and might complete before any observer can subscribe to it. In this case, AsyncSubject can still provide the last value and publish it to any future subscribers.
Two more comments:
in your plunker : this._coldObservable = emitter.share();. Using share returns a hot observable!
EventEmitter actually extends subject in the first place
UPDATE :
Wrapping an EventEmitter around an Rx.Observable:
function toRx ( eventEmitter ) {
return Rx.Observable.create(function ( observer ) {
eventEmitter.subscribe(function listener ( value ) {observer.onNext(value)});
// Ideally you also manage error and completion, if that makes sense with Angular2
return function () {
/* manage end of subscription here */
};
};
)
}
Once you have that Rx.Observable, you can apply share(), shareReplay(1), anything you want.
My bet is that the Angular team will sooner or later propose a brigding function but if you don't want to wait, you can do it yourself.
ReplaySubject is doing what I was looking for. #robwormald provided a working example on gitter I slightly modified to better demonstrate.
Exposing HTTP response:
import {Injectable} from 'angular2/angular2';
import {Http} from 'angular2/http';
import {ReplaySubject} from '#reactivex/rxjs/dist/cjs/Rx'
#Injectable()
export class PeopleService {
constructor(http:Http) {
this.people = new ReplaySubject(1);
http.get('api/people.json')
.map(res => res.json())
.subscribe(this.people);
}
}
Subscribing multiple times:
// ... annotations
export class App {
constructor(peopleService:PeopleService) {
people.subscribe(v => {
console.log(v)
});
//some time later
setTimeout(() => {
people.subscribe(v => {
console.log(v)
});
people.subscribe(v => {
console.log(v)
});
},2000)
}
}
Full plunker
EDIT: the BehaviorSubject is an alternative. In this usecase, the difference is the initial value, for example if we want to display content from cache before updating with the HTTP response.
I'm not so experienced/elegant programmer. I hope my question is understandable.
I have used java/c++ for most part of my life so my mind is object oriented. Then I learned python and I enjoyed very much functional mind. Now I'm approaching js. It is challenging because it is function based (and I liked it a lot), functional (with underscore I have a lot of pythonic iterutils methods) and asynchronous (it hurts).
A lot of time I have some back-end sync procedural flow like:
if exist(user):
foo(user)
# ok
else:
add(user)
# user added
Now I have to handle this with the so called callback hell:
exists(user, function(userExist) {
if( userExist ) {
foo( user, function(fooResult) {
/* ok */
});
} else {
add( user, function(addResult) {
/* user added */
});
}
});
Sometimes the controls are more nested: check for token, if token is valid check for user exists, if user exists check for valid parameters, then check for no error in foo on user, etc…
Those controls are simply synchronous and imperative-like. Nothing else to say. While with sync language like python I can handle this is (not elegant but at least) readable code with the use of small function that returns values , with javascript I don't know how to refactor things in readable small function. All functions I wrote does not have any return statements but just callback(something_weird_defined_in_caller_function) and I lost myself.
I don't think that promises are good on my case because are more for piping events, IIUC. So I'm looking for some pattern using async library to handle this case:
- continue to execute functions in series only if the previous one succeeded.
Please any help will be appreciated.
I don't think that promises are good on my case because are more for piping events, IIUC
No. Promises don't represent a stream of events to pipe, but a single result that will arrive later (asynchronously). This is exactly what they were made for, and they will give you imperative-like looking code (instead of a nesting callback hell).
Admittedly, you still need nesting for control structures. The code that you presented won't look much different except for the callbacks going into .then() calls, and promises being returned.
However, you can also use exceptions for control flow. Instead of using exists that returns a Promise<boolean>, you could use a find function that returns a Promise<User> which would get rejected when the user doesn't exist. It could be used like
find(user).then(function(/*user*/) {
return foo(user) /* .then(function(fooResult) {
…
}); */
}).catch(UserNotFoundError, function(e) {
return add(user) /* .then(function(addResult) {
…
}); */
});
Which of those to choose is debatable, rejections should be exceptional.