Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Do Observables make sense for http? #5876

Closed
thelgevold opened this issue Dec 14, 2015 · 71 comments
Closed

Do Observables make sense for http? #5876

thelgevold opened this issue Dec 14, 2015 · 71 comments

Comments

@thelgevold
Copy link
Contributor

I don't want this to sound too critical :-).

I just have a few questions/concerns about Observables in the context of Angular 2.0 http.

Since I started using http in Angular 2.0 I have always wondered why a decision was made to go with Observables over promises.

Don't get me wrong, I think observables (with or without RxJs) is cool and I've enjoyed learning about it, but when it comes to plain http calls, I am struggling to see a clear advantage over regular promises.

One of the arguments I've seen is that promises are only good for one time calls, but a stream can be left open indefinitely. My view though is that "one time" http calls probably make up the 90% use case for http. I can definitely see an application of the open ended streams, but in my experience I see it more as a one off case in most business applications. Basically now, I find myself using Observables in a way that just mimics promises.

I see other applications of Observables though. Some examples I've seen include streams of events (e.g. click event patterns etc). I think it definitely has merit, but it seems to me that we are trying to "shoehorn" the 90% use case into the 10% use case with this.

I am not against incorporating alternative technologies, but I question if this pivot to Observables is worth the reeducation of developers when it comes to the http calls. I wonder if it would make sense to offer a lean promise based http implementation to the masses and encourage people with real streaming needs to use RxJs directly instead? I think that will allow us to benefit from the best of both worlds and better enable us to pick the best tool for the job.

Just to be clear: My questions/concerns about this predates the new approach of having to bring in RxJS dependencies piecemeal :-).

-Tor

@adaojunior
Copy link

I have little experience with Angular 2.0, but that was the same concern I had when trying the new angular. For most of my Http calls a promise would be the best way to go.
But I'm curious to know why the Angular Team made the decision to use Observables.

@ericmartinezr
Copy link
Contributor

If you guys make me choose between a car that can transtorm into Optimus Prime and a regular car just to go from A to B, I would totally go with the first one :D

@0x-r4bbit
Copy link
Contributor

Observables are way more powerful. We can easily create code that "retries" http calls, in case the first one failed an similar things. You can't do this with promises at all. In addition, another thing you can't do with promises is cancelling calls. Just think of the use case where you build a type ahead component and with each keystroke you have to throttle 300 ms before you send the actual Xhr. This is very easy with observables, but not only that, with observables we can make sure that we always execute the return call in the right order + we can cancel calls.

Promises are just not suited for that.

I wonder if it would make sense to offer a lean promise based http implementation to the masses and encourage people with real streaming needs to use RxJs directly instead?

You can turn any observable into a promise by calling .toPromise() on it. It's a stream with a subscription that ends as soon as the first call comes back.

Does that make sense @thelgevold ?

@thelgevold
Copy link
Contributor Author

@PascalPrecht I see the advantages in the cases you describe but I guess time will tell if the benefits will warrant the new direction. I could be wrong about this, but I am not 100% convinced that the use cases you describe have been huge pain points for developers in the past. Generally I find most http needs to be super simple and typically amounts to fetching some simple json without the need to either cancel or retry.

Not trying to fight the change, just not 100% convinced yet that it's necessary as the core implementation :-). Especially now that the api is a bit cumbersome with piecemeal includes.

I don't agree with the statement that promises don't allow you to guarantee correct order of execution though.

@0x-r4bbit
Copy link
Contributor

I don't agree with the statement that promises don't allow you to guarantee correct order of execution though.

That's right, you can emulate the actual order by chaining them. My actual point was that e.g. you could say you fire 10 XHR calls, and you're only interested in the very last one, while the other 9 are still pending, which is a very common case when you build search inputs etc. With Observables all other requests can be cancelled and don't eat any more resources.

And again, you can always fallback to promises if you want. No one forces you to use observables.

@thelgevold
Copy link
Contributor Author

I agree, using promises is definitely an alternative.
I will likely experiment with both - at least for my ongoing blog/POC work.

I will let it go for now :-), but still not convinced that Observables have enough real use cases in the context of http to warrant the switch. The ability to cancel and cherry pick requests is nice, but I also question the underlying architecture of a system that constantly gets into a state where requests have to be cancelled, ignored or retried.

The search box might be an exception, but request control and predictability is also part of a good design. Performance is still impacted if you fire 9 out of 10 requests needlessly. They still hit the server even though you have the ability to ignore the results.

@yfain
Copy link

yfain commented Dec 15, 2015

IMO, learning the basic syntax of making HTTP requests with Observable is pretty easy. Even if you're not interested in cancellable requests or retries, using the same approach to processing data (regardless if they come from HTTP, WebSocket, or an event) is beneficial.

@thelgevold
Copy link
Contributor Author

@yfain Yeah, the syntax is pretty straight forward. I've played around with it and found a solution to most of my needs (plain call, chaining etc) in my Angular 2.0 sample project.

I think though that $http worked well in Angular 1.x and was one of the concepts that could have been allowed to migrate over as a default implementation. After all it's pretty much based around standard promises.

There has also been a lot of back and forth about how to implement the Rx integration in Angular 2.0, which I assume contributes to delays in getting Angular 2.0 to market. I suspect things are still in flux when it comes to this.

@robwormald
Copy link
Contributor

I should preface with this that 8 months ago I was literally making the same argument @thelgevold is currently, and now I'm probably the most vocal advocate for RxJS and Observables on the team :)

First - the simple promise based API you want already exists, native to the (modern) browser - it's the Fetch API, and it works just fine with Angular2. It's not without its own set of issues - that epic thread is mostly due to the fact that Promises, as they exist today, don't have a semantic way of describing cancellation. Is a cancelled Promise a success or an error? Neither? Something else? To be decided...

One of our big focuses in angular2 is mobile - not being able to cancel requests we no longer care about (as @PascalPrecht mentions) is a dealbreaker. Rather than trying to shoehorn that into Promises, Observables provide this functionality out of the box.

Beyond cancellation - Promises are not re-usable. If a promise fails (again, think spotty network connection on a mobile device) you have to completely re-initialize the whole request. Observables:

http.get(url)
  .retry(3)
  .subscribe(...);

or retry 3 times with incremental backoff...

http.get(url)
  .retryWhen(attempts => {
    return Observable
      .range(1, 3).zip(attempts, (i) => i)
      .flatMap(i => Observable.timer(i * 1000));
  });

That only touches the surface of the sort of stuff that's trivial to do with Observables.

I'd agree that if you look at http in an isolated context, promises might seem to make more sense (excluding the above) - but I'd argue that in the context of a reactive application, you're not dealing with a single request - you're dealing with a stream of actions that get transformed into requests...

this.results = someFormInput.valueChanges //observable of input changes
  .debounceTime(150) //debounce for 150ms
  .switchMap(query => http.get(`http://api.com?q=${query}`)) //make request
  .map(res => res.json()); //map to json

Again, only touching the surface of what we can do here. For what it's worth, RxJS beat Angular2 to beta (by one day!) and @Blesh and co have been great partners for us - they are certainly not contributing to any delays to angular2.

I'm a huge fan of Promises, and it's easy to turn an Observable into a Promise - a Promise, after all, is effectively a Observable of a single value. The opposite is very difficult to do.

My 2c. Http isn't yet complete, and as its a standalone module, the option for what you use is completely up to you, with no penalty for opt'ing out. That said - stay tuned. Lots of interesting stuff to come on top of this. See angular's tactical prototype, which again is built on top of Observables : https://github.com/angular/tactical

Closing this as this decision is pretty much made, but absolutely feel free to continue discussion or ask questions :)

@benlesh
Copy link
Contributor

benlesh commented Dec 19, 2015

In a single page application, promises are a sub-optimal choice for HTTP, because promises cannot be cancelled.

  1. User clicks a link and starts loading a view
  2. Routing/view logic starts loading some data via promise-wrapped AJAX.
  3. User changes their mind and leaves the view.
  4. It would be nice to cancel the AJAX request and resolution logic from step 2, but you can't because Promises don't support that.
    5 Go to 1

Promises have a few problems:

  1. They can't be cancelled, so you're going to execute code you didn't have to on occasion because of Promises. This gets worse on resource-constrained devices.
  2. They "trap" errors. If you throw inside of a then block, you must have another then block after or the error is lost. And best you might get a helper message in console, but there's no where to globally catch it like other unhandled errors (window.onerror)
  3. They can only handle one value.
  4. There's no filtering, etc.
  5. then is used for both mapping functionality and side-effects. Makes it a little harder to determine where side-effects occur.

All-in-all when I see heavy promise use in code, I suspect there could be bugs or unwanted behavior.

@thelgevold
Copy link
Contributor Author

@robwormald @Blesh
I am open to Observables in general, but I am still not convinced that the advantages are as clear in the case of run of the mill http requests.

Cancellation:
Granted, it's nice to be able to cancel requests, but I don't consider that a very frequent operation. Besides on devices, cancellation of navigation may often be of just theoretical value. By the time you get back to your small "hamburger" menu to navigate somewhere else, your initial call has likely already finished :-) I also think we could get the cancellation benefits by extending the promise standard to include a cancellation call since that seems to be the most cited benefit. I see this as an added feature of Observables - not something that is necessarily tied to just Observables. I remember plain xhr requests in jquery contained a request cancellation feature. In any event, my view is that the benefits of cancellation does not weigh up for the re-education of devs and shift in approach. It has also presented a challenge when it comes to the Angular 2 http api since it's too heavy to bring in the entire library.

Side Effects:
I agree that junior developers often set global variables/side effects in promise handlers, but Observables are also callback oriented, so the same devs are likely to do the same thing using Observables. I always make sure to return results up the chain, which in my opinion makes for very clean promise handling - without side effects. The limitation of only one result is not a real issue since you can return any object from anywhere in the chain. I can also reuse the same promise over and over again as an easy caching mechanism.

Errors:
It's true that you need a subsequent 'then', to handle error, but the 'then().catch()' pattern is a great convention here. I never use 'thens' with two callbacks since it won't handle issues in the success handler itself. Instead I use 'catch' since it makes for more readable code. (BTW I understand that catch is just then in disguise)

someService()
.then(singleCallBack)
.catch(singleErrorCallback)

That said. I am very impressed with the RxJS implementation in general, so please don't see this as criticism of your library. It's just my view that it's not as clear that it's the best choice as the default http implementation. However, I see great benefits for people with real streaming needs though.

P.S.
Your assumption that promise based code is buggy in general is far too broad..... :-)

@benlesh
Copy link
Contributor

benlesh commented Dec 20, 2015

You're welcome to use whatever you like. There is nothing stopping anyone from using any number of promise-based AJAX implementations. As long as you're aware of the inadequacies, and you're willing to work around them, you'll be fine.

However, it's my advice, if you're going to allocate an object for async abstraction, to use an observable for the benefits that have been mentioned here and many times elsewhere.

Promises are "okay"... You just need to know where they might get you.

@thelgevold
Copy link
Contributor Author

Ok thanks for all the points that have been made regarding this. I will definitely keep exploring Observables and dive into some of the more advanced use cases.

Currently I think I have all the "normal" Observable use cases covered in my blog: http://www.syntaxsuccess.com/viewarticle/angular-2.0-and-http

Anyway, I will take a deep dive and report back later to let you know if I have become a believer :-)

@jamesthurley
Copy link

I just posted about this over on #2684, but having read this thread I'll try to answer my own question...

Am I right in saying the reason Angular doesn't simply return Promises and let people turn them into Observables if they feel like using RxJS is that cancelling the resulting Observable wouldn't cancel the underlying HTTP request? But by returning Observables natively, cancellation does cancel the underlying HTTP request?

@benlesh
Copy link
Contributor

benlesh commented Dec 23, 2015

Am I right in saying the reason Angular doesn't simply return Promises and let people turn them into Observables if they feel like using RxJS is that cancelling the resulting Observable wouldn't cancel the underlying HTTP request? But by returning Observables natively, cancellation does cancel the underlying HTTP request?

That's correct.

@benjamingr
Copy link

@robwormald
Copy link
Contributor

@benjamingr oh dear, I'd hoped we'd seen the last of that thread...

@benjamingr
Copy link

We have, it's an historical reference at this point demonstrating the way decisions are bring made in that project :)

Clearly the users' best interests come first and not how one can push what spec to the TC. I use Aurelia, and I'm not really an Angular 2 user (at least yet) - so I'm all for pushing observables (which I use) to the spec at the expense of Angular users now. I just don't want to see the same happen to Aurelia :)

@jamesthurley
Copy link

I think as more people move to Angular 2 this question is going to keep re-occuring. The benefits of using observables for single HTTP requests is just not immediately obvious to most people when they first encounter it.

Even the RxJS README.md says "Promises are good for solving asynchronous operations such as querying a service with an XMLHttpRequest, where the expected behavior is one value and then completion". So that doesn't help explain things!

It's probably worth distilling all the reasoning in these various threads into a helpful piece of documentation that can be referred to in future when people ask the question.

Showing how to implement a more advanced retry function based on the failure reason would also be quite enlightening, and something most people will need. Seeing so many examples using .retry(3) or .retryWhen(..) is frustrating, as you wouldn't actually use them in real life HTTP requests.

@benjamingr
Copy link

Retry and cancellation are examples that can be translated to promises trivially. Observables shine in more complicated examples like autocomplete, infinite scrolling, etc.

If you want to show where they shine you need to show use cases promises are not capable of.

@fxck
Copy link

fxck commented Dec 28, 2015

@benjamingr please do show me how to trivially cancel a promise :)

@jamesthurley
Copy link

I think that google groups thread contains a lot about cancelling promises, no need to start that again here! :)

@benjamingr
Copy link

@fxck here http://bluebirdjs.com/docs/api/cancellation.html#what-about-promises-that-have-multiple-consumers although the Internet is full of examples and Angular 1 had (quirky, but still) cancellation so there's that.

@thelgevold
Copy link
Contributor Author

I think the Google thread was an interesting read. I am still planning on using both Promises and Observables, but I think better documentation is key here. Ideally we should have "neutral" documentation describing the pros and cons to both approaches. Currently a search for toPromise() does not yield a single result in the API preview :-)

Perhaps the documentation should describe when you actually might have a clear benefit from Observables, but right now it's very biased towards just Observables.

I also wouldn't mind a more explicit "opt-in" approach to either one based on application needs and developer experience. My key points has always been that I don't necessarily agree that the ability to cancel the request necessarily outweighs the burden of re-educating developers who might not even utilize the cancel feature.

Instead of defaulting to Observables we could make it more explicit, so that people can educate themselves on when one makes sense over the other.

http.asObservable().get('/myUrl').subscribe(...);

vs

http.asPromise().get('myUrl').then(...)

Better documentation describing the pros and cons of both might make it easier to pick the one that is best suitable for your own specific needs/team skills.

@benjamingr
Copy link

@thelgevold no no, we should Observables as much as possible so they have adoption. We get a smoother sail for the proposal through the TC stages and into the language.

@benlesh
Copy link
Contributor

benlesh commented Dec 29, 2015

@benjamingr: To be fair, cancellable promises aren't really the "Promises" anyone is talking about in this thread. This is about standard promises vs the observable type that's been in languages for years.

Cancellation completely breaks the one good thing about promises by making them mutable. But that's a debate for another day.

@benjamingr
Copy link

@Blesh cancellation doesn't make promises very mutable - I agree that it's not as pretty. Cancellation for promises signals "disinterest" and not "abort" so all a consumer can do is signal they're not interested in the return value (and not change it, handlers will not fire - with the exception of finally). Analogous to return on a generator.

This is less about promises IMO and more about unicast vs multicast semantics. Being always multicast means promises have to refcount in order to have sound cancellation meaning abort semantics are not an option.

Promises also do not "swallow errors" - not even native ones. Promises will fire an event when an error happened but was not handled (window.addEventHandler("unhandledrejection" and process.on("unhandledRejection" in node).

I'd just like to emphasize I'm for Observables in Angular's http now because it'll help push them in the spec and battle test them by many users. While frameworks have a 5 year lifespan Observables will stay in the language for good and I get to work with a good default functional abstraction for handling events (and not DOM event emitters :S).

What I think we need for ng2 is to sell Observables to the users with motivating examples in the spec - these examples should not be "retry" or "cancel" but things promises can't do elegantly like autocomplete, working with websockets, progression and anything with "many" semantics which is painful before observables and trivial with them.

@thelgevold
Copy link
Contributor Author

@robwormald
I know you cautioned against the use of fromEvent. I tried it and I totally agree - it's not a good idea.

I think what really bothers me abut this is that you end up with a global "click" subscription, so you have to store some of the state in the DOM.

I find that this solution is too closed off for input since it's a global registration.

<table id="tableId" class="table">
    <tr [ngClass]="{active: isActive('usa')}"><td id="usa">USA</td></tr>
    <tr [ngClass]="{active: isActive('denmark')}"><td id="denmark">Denmark</td></tr>
    <tr [ngClass]="{active: isActive('germany')}"><td id="germany">Germany</td></tr>
    <tr [ngClass]="{active: isActive('argentina')}"><td id="argentina">Argentina</td></tr>
</table>

ngOnInit(){

    this.pendingRequest = Observable.fromEvent(document.getElementsByTagName('td'),'click')
        .switchMap((r:any) => {
                               this.activeCountry = r.target.id;
                               return this.http.get('./country-info/' + this.activeCountry + '.json')
                             })
       .map((res: Response) => res.json())
       .subscribe(r => this.capitol = r.capitol);
}

I can't picture a perfect API here, but I would love a way that hides the creation of the initial Observable without any hard coded DOM references.

Would it be an idea to introduce an Observable group? You can then decide what type of events you want to associate with the group.

<div ObservableGroup=['group1',{country:'usa'}]>USA</div>
<div ObservableGroup=['group1',{country:'germany'}]>Germany</div>

Observable.fromGroupAndEvent('group1','click')
.switchMap((r:any) => {
                                       this.activeCountry = r.country;
                                       return this.http.get('./country-info/' + this.activeCountry + '.json')
                                  })
            .map((res: Response) => res.json())
            .subscribe(r => this.capitol = r.capitol);

@robwormald
Copy link
Contributor

@thelgevold if you look in #4062 there's some ideas on what the implementation might look like. Let's keep discussion in there as that's an open issue.

@benjamingr your input would be most welcome there as well - i'm using a similar redux-like-with-rx pattern in angular2 and it's the missing link for me.

@benlesh
Copy link
Contributor

benlesh commented Dec 31, 2015

Quick side note: in both @benjamingr's fiddle and @thelgevold's code example I can see

source.flatMap(x => doSomething(x)).map(r => selectResult(r))

and

source.switchMap(x => doSomething(x)).map(r => selectResult(r))

... (not condensed on purpose so you can see arguments)... It should be:

source.flatMap(x => doSomething(x), (x, r) => selectResult(r))

and

source.switchMap(x => doSomething(x), (x, r) => selectResult(r))

It's easily the most common perf gaff I see in Rx. (sometimes it's intentional, I suppose, but still)

Just wanted to shout that out. I do it too, sometimes. Or at least I used to do this a lot. I'm sure it's still in some of my older code.

@benjamingr
Copy link

Since this thread turned out to be general discussion of observables (don't get me wrong, it's a good thread and people in the future might refer to it):

@Blesh would you mind elaborating a little on why it's a perf gaff? I assumed it'd be "about as fast" and never really considered it.

By the way - In all honesty the vast majority of our RxJS code is:

  • In RxJS 2 (some is in 4, but no code is in 5 yet, I didn't think/know it was production ready yet).
  • Not very performance sensitive, the parts on the server that use RxJS create a few observables when the app starts and then reuses them with promises assimilated for singular things. The parts on the client never had any performance issues because of RxJS itself.

I'm sure I have a lot less Rx experience than you netflix folk who use it at much larger scale and on more projects. We do more typical (and probably less interesting) things with Rx. I'd be interested to hear where you ran into performance problems.

@benjamingr
Copy link

@robwormald

@benjamingr your input would be most welcome there as well - i'm using a similar redux-like-with-rx pattern in angular2 and it's the missing link for me.

Then why do you need Angular 2? What does it actually give you on top of just using Rx with a virtual dom with components?

@robwormald
Copy link
Contributor

What can I say? I like my templates...

On Dec 30, 2015, at 11:43 PM, Benjamin Gruenbaum notifications@github.com wrote:

@robwormald https://github.com/robwormald
@benjamingr https://github.com/benjamingr your input would be most welcome there as well - i'm using a similar redux-like-with-rx pattern in angular2 and it's the missing link for me.

Then why do you need Angular 2? What does it actually give you on top of just using Rx with a virtual dom with components?


Reply to this email directly or view it on GitHub #5876 (comment).

@benlesh
Copy link
Contributor

benlesh commented Dec 31, 2015

@Blesh would you mind elaborating a little on why it's a perf gaff? I assumed it'd be "about as fast" and never really considered it.

Simply put, it incurs more subscriptions. Each operator requires a subscription to the source observable under the hood. That's another observable, another subscription, another observer, extra function calls down your chain, etc. Overhead. However, if you use the resultSelector argument in mergeMap/flatMap or others (switchMap, concatMap, fromEvent, et al), that overhead is reduced to just calling the function on the result directly.

Generally, it doesn't make much difference for the average app. But when perf matters (say at scale, or in a busy, real-time UI), you want to limit subscription/operator depth if possible.

@benjamingr
Copy link

That's another observable, another subscription, another observer, extra function calls down your chain, etc. Overhead.

I think I might have a(nother) mental gap in my understanding of RxJS then. Why do you actually have to create all that when .map is attached?

I'd expect Rx to optimize this under the hood and not really create a new subscription/observable/observer until it has to (that is, I'd expect it to "forward" the original subscription and then copy on write when a second subscriber is attached). The most common case at least for things like http calls (but definitely not the only one) is to have a single subscriber. I guess I'd expect RxJS to optimize for that (like bluebird does).

@benlesh
Copy link
Contributor

benlesh commented Dec 31, 2015

What does it actually give you on top of just using Rx with a virtual dom with components?

I think one of the more compelling things about Angular 2 will be build-time tooling around statically analyzing templates. You could, in theory, analyze all of your templates for your components and do things like generate graph queries that those components needed to use, and/or compile other metadata to make things like Falcor more efficient. There was a talk on some of these features I think at AngularConnect, or perhaps it came up while chatting with @IgorMinar, I'm not sure.

That wouldn't be as possible in a VDOM scenario, where the view could be built in anyway JavaScript can manipulate an object. (It's possible, but clunkier, and unlikely to work in every scenario). Personally, I love using VDOM, but it does violate the Rule of Least Power a bit. That said, VDOM fits 99% of my needs for projects I currently work on at Netflix.

@benlesh
Copy link
Contributor

benlesh commented Dec 31, 2015

I'd expect Rx to optimize this under the hood and not really create a new subscription/observable/observer until it has to

I agree. We're working on that. But currently no version of RxJS will do this.

Likewise, there could be similar optimizations for operators that are completely synchronous like filter, map and scan when chained back to back.

@robwormald
Copy link
Contributor

You could, in theory, analyze all of your templates for your components and do things like generate graph queries that those components needed to use, and/or compile other metadata to make things like Falcor more efficient. There was a talk on some of these features I think at AngularConnect, or perhaps it came up while chatting with @IgorMinar, I'm not sure.

@alxhub covered this in his talk at AngularConnect - https://youtu.be/bVI5gGTEQ_U?t=905 - Template Transforms.

A falcor example:
The sugared syntax, where graph is a sort of flag/hook that a custom template transform can look for:

<div #user="graph.users[123]">
  {{user.firstName}} {{user.lastName}}
</div>

that desugars into the longhand falcor syntax

<div #user="graph.deref(['users', 123]) | async">
  {{user.get(['firstName']) | async}}
  {{user.get(['lastName']) | async}}
</div>

@benjamingr
Copy link

I moved the optimization discussion to the Rx repo at ReactiveX/rxjs#1121 (I hope that's ok).

@benjamingr
Copy link

I think one of the more compelling things about Angular 2 will be build-time tooling around statically analyzing templates.

JSX templates in React are just JavaScript, it's trivial to statically analyze and transform (it's just a regular babel transform, no additional parsing). Much easier than any tooling for a real dedicated template format - errors are detected by all existing JS tooling that understands the transform. The type system (flow) isn't too shabby either. It's not like Angular templates aren't Turing complete after all - but they can't leverage all the existing static analysis tools JS has.

Since you import all other components you use it's very easy to generate dependency graphs (we need those for hierarchical bundling anyway). We really want to move to something like falcor (I'm a big fan) but our backend runs C# and nothing that runs falcor on .net is production ready. When we do - I suspect we can use our dependency map (or our injector, if I get my way) to do that.

I'll watch the AngularConnect talk. Maybe it'll make be a believer :)

@eliOcs
Copy link

eliOcs commented Oct 22, 2016

All the examples that are defending the use of Obsevables are showing the edge cases where using them make sense.

¿Why don't you show an example that represents the common use case using Observables?

For example if you do a standard request:

this.http.get('./country-info/' + country + '.json')
    .map((res: Response) => res.json())
    .subscribe(res => this.capitol = res.capitol);

When I read this code I understand the following: for each result that the http.get returns I'm going to transform it into json and then update the capitol property with the result. But http.get will only ever return one result, so I'm subscribing for only one update which is very misleading.

How can you defend this type of code:

['{ "capitol": "Washington DC"}']
    .map((body) => JSON.parse(body))
    .forEach((result) => this.capitol = result.capitol);

Instead of:

const body = '{ "capitol": "Washington DC"}';
const result = JSON.parse(body)
this.capitol = result.capitol

I can't get my head around it.

@fxck
Copy link

fxck commented Oct 22, 2016

That's not how it works. Doing subscribe is in this case literally the same as using promises with then. You won't ever have to do that foreach yourself, nor does it work like that inside, it's not an array, it's a stream.

@eliOcs
Copy link

eliOcs commented Oct 22, 2016

You are forcing the use of a stream of events which can handle unlimited events when there is only one event to handle.

@fxck
Copy link

fxck commented Oct 22, 2016

Yes, and why do you, as the consumer, care? Performance wise, it's the same, apart from using subscribe function instead of then it's all the same for you. If you don't want to use any of the extra handy features that comes with it being observable, you don't have to, use it as if it were a promise. You can even turn it into a promise just by doing .toPromise(). Also none is forcing you to use Http, you can use fetch or whatever else, the whole framework is written in a way that allows you easily swap libraries.

@benlesh
Copy link
Contributor

benlesh commented Oct 25, 2016

this.http.get('./country-info/' + country + '.json')
    .map((res: Response) => res.json())
    .subscribe(res => this.capitol = res.capitol);

vs

this.http.get('./country-info/' + country + '.json')
    .then((res: Response) => res.json())
    .then(res => this.capitol = res.capitol);

... only the second one can't be cancelled and will trap your errors. In a single page app, cancellation is important because when you change routes you don't need to finish loading data from the previous route. It's wasted computational cycles, garbage collection, memory allocation, etc, etc. Promises just aren't the right type for HTTP in a single page app.

and then when you want any sort of resiliency against your network going out, or you want to retry every second on failure you can just add one line with RxJS... Total control. Not so with promises.

this.http.get('./country-info/' + country + '.json')
    .retryWhen(error$ => error$.switchMap(err => navigator.onLine ? timer(1000) : fromEvent(document, 'online')) // ONE LINE ADDED
    .map((res: Response) => res.json())
    .subscribe(res => this.capitol = res.capitol);

In any case @fxck is 100% correct that you can use Promises and not Angular's Http lib if you choose. In fact, even with RxJS code, anything that takes an Observable will also accept a Promise. It's all very interop friendly by design.

Much ado about nothing, really. I've had this discussion so many times I can't count them, so I don't want to engage much more, but I just wanted to point out a few basic points on this.

The only argument in favor of promises I think actually has some merit is that they're multicast by default... which is something you have to opt-into for observables.

@thelgevold
Copy link
Contributor Author

I am using observables almost exclusively now for Angular 2 http, but in most cases, promises would have worked equally well.

Question to the group: With async/await becoming a "thing".... Will this perhaps start this debate back up again?

I understand that async/await does nothing to bridge the functionality gap between promises and observables, but maybe the new syntax will be appealing to some.

In my view async/await aims to make async code flow feel more synchronous, which might be easier for junior developers to grasp.

Again, the mentioned arguments in favor of observables will obviously still stand even with async/await.

@benlesh
Copy link
Contributor

benlesh commented Oct 25, 2016

Async await currently lacks cancellation. When it does finally support cancellation via tokens, observables will also support tokens seemlessly.

Other things: promises are always asynchronous. Observables can be synchronous, this makes them easier to deal with and more performant with regards to SSR.

Additionally WRT SSR, observables don't trap errors completely, which means that node services can opt into the "panic" behavior that is desirable for better debugging of node processes (via core dump analysis)

This is a really long thought out decision. There have been volumes of blogs and talks about the benefits of observables over promises in common web app scenarios

Also... Again... There's nothing stopping you from using async await or promises wherever you want.

@benjamingr
Copy link

... only the second one can't be cancelled and will trap your errors.

@Blesh you've got to stop saying that, there are plenty of real reasons to use observables in Angular 2 - promise cancellation is as-specced as observables and promises don't trap errors. Retrying is again, trivial.

Async await cancellation is also, as far ahead as observables in the spec. I disagree with the SSR point - but meh.

The argument for observables is having a unified API and to push them in the spec, that should be enough.

@benlesh
Copy link
Contributor

benlesh commented Oct 28, 2016

@Blesh you've got to stop saying that

No I don't. ¯_(ツ)_/¯

@ahmad-moussawi
Copy link

I totally agree with @thelgevold, I've spent more than 5 hours trying to convince myself with Observables instead of Promises, and found that it's adding too much complexity for api responses.
especially in such cases when you get onetime response.

What developers really need is the flexibility to manage the returned items inside the response and not the response itself.

for instance imagine an api call that returns list of cars Car[], the http module will return Observable<Car[]>, while what really needed is to manage the list items Observable<Car> and not the response itself, in this case we could get benefit of the Rx Operators on the data.

Even more, I think using Lodash or something similar (on the data level and not stream level) instead of RxJs for such situations will be more useful. Promise<LodashExplicitArrayWrapper<Car>>.

this.http.get('/api/cars')
.then(q => q.sortBy(x => x.price ) )
.then(q => q.map(x => x.name) )
.then(q => q.value() ) 

@ghost
Copy link

ghost commented May 11, 2017

It's true you really don't need any arithmetic operators other than + and that if you resort to using + most of the time you might feel like those other operators are just complicating matters and forcing you to learn new approaches. And so it is with this discussion about promises.

@msieurtoph
Copy link

One big difference between Promises and Observables, is that Observables are functions!
You need to subscribe to get the action done. Which is not the case with Promises.

I understand the power of Observables and the lacks of Promises.
But I got really confused because sometimes you need to execute something that return a Observable but you dont care about the output, you just need the job to be done, you dont need to chain any other instructions after.

Using Promises, I could do : return http.get(...) or return http.get(...).then(...).
In both cases, the http call was executed.

Using Observables, I have to chain a subscription absolutely. Because Observables are just functions. If I do not, the http call is never executed :
the simple return http.get(...) won't work.
I need to add a blank subscription : return http.get(...).subscribe() to get the http call executed.
And by the way, I need to share() it too, in case of any other subscription after. If I do not there will be one call by subscription...
So the final instruction is : return http.get(...).share().subscribe() which is more indigestible than the simple Promise instruction return http.get(...) ... and I think it is too bad about the Observables.

@stanislavromanov
Copy link

this.http.get('./country-info/' + country + '.json')
    .map((res: Response) => res.json())
    .subscribe(res => this.capitol = res.capitol);

vs

const res = await this.http.get('./country-info/' + country + '.json').json();
this.capitol = res.capitol;

I totally don't see the point of Observable in this case TBH.

@angular-automatic-lock-bot
Copy link

This issue has been automatically locked due to inactivity.
Please file a new issue if you are encountering a similar or related problem.

Read more about our automatic conversation locking policy.

This action has been performed automatically by a bot.

@angular-automatic-lock-bot angular-automatic-lock-bot bot locked and limited conversation to collaborators Sep 12, 2019
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
None yet
Projects
None yet
Development

No branches or pull requests