Single asynchronous primitive vs. several.

763 views
Skip to first unread message

inglor

unread,
Apr 4, 2015, 4:54:33 AM4/4/15
to angular-...@googlegroups.com
So, since it's a highly debated topic here - I figured I'd spin this out to a new thread.

Shouldn't everything be an observable?

Some people here argue that observables are the correct way to model HTTP in Angular 2.0. I don't disagree, in fact I think observables are a really useful way to model data flow when data is multiple values. That said - I think that returning more limited types is immensely useful from an API perspective [1] - it conveys intent better and it helps developers understand APIs and find out errors. I think we should prefer APIs that return as constrained of types as possible.

Addressing some of the arguments for an observable centric API:

 - Promises are as easy to cancel as observables are. In general both observables and promises suffer from problems with regards to cancellation - the primary way to avoid the issues raised about the multiple-subscriber tree cancellation problem is to use a type limited to a single-subscriber like a Task or a Task analogy for an iteration of values [2] [3].
 - Promises are as easy to retry as observables [4] - numerous implementations exist.
 - Promises also offer the same performance value as "cold observables" since they are immutable and cache by design.

Just to put everyone on the same level: streams and observables are duals the way promises and callbacks are duals. The way node models streams with data events, error events and end events directly parallels to observables as represented by RxJS - so for all purposes here - the two terms are synonymous. The outcast and interesting party to consider here is pull iterators (rather than observables and events which are push). 

Observables are really cool

Now that these arguments are out of the way - I'd like to make a case for observables:

 - A lot of time APIs return multiple results and not just a single result - a promise simply is incapable of representing this.
 - With large HTTP requests and responses it is beneficial to get updates during the time the response is read with partial data and not just with end-data. This is also useful to chain data from one place to another without keeping it all in memory in one place. It's also faster in this scenario.
 - We never got progress events right in Angular 1.x because of API problems with promises - these are problems we would not have with observables. 
 - Not everything is a single value and  observables are a beautiful - proven and useful abstraction over multiple values over time.

Promises and tasks are also really cool

Promises to covey single value results with multiple subscribers and tasks to represent a single value result with a single subscriber are also really useful. Promises are a much simpler abstraction over value + time since it's built for a single value. 

Promises have only 3 states (pending, fulfilled and rejected) and they only state-transition once - once they do they're settled and can't change. Promises are trivial to aggregate and work with and everyone in the Angular community already knows how to use them and understands them at least at a basic level. Promises and similar constructs are used throughout many programming languages in language libraries (C#, C++, Python, JavaScript, Dart, Scala and even Java) - they are a well understood and powerful abstraction for value + time and they work well.

Tasks are basically like promises but only allow one subscriber - this allows for cancellation to work soundly because there are no branching issues and is explained in KrisKowal's gtor [5]. This also allows for progression to flow through a task chain soundly - which means http progress events can be modelled through a task.

Just like returning an array with a single number from a `Math.abs` method works (since an array can contain a single value) do does returning an observable for a single value - but you want to provide the most constrained return API.

A promise does not represent an HTTP request and neither does a task.

I think we all understand that a promise/task does not really represent an HTTP request cycle very well - there are cases where we're interested in multiple values (streaming the body in either direction), this is a fundamental capability of observables which is really useful. On the other hand - an observable allows for a very wide interface - you're not sure it'll only resolve with one value when you read it and giving it as the API does not sound like a super useful API choice for me

Why not both?

In my opinion, the new API should not limit itself to a single hammer as an abstraction - there are multiple types of data we're dealing with here and we should use promises observables and tasks throughout the API - each where it makes sense. Each of these offers a big benefit over the others in several different scenarios. One option would be:

 - Making an http request takes a Request object (or just URL + data) and returns a `Task<Response>`. Since it's a task it can be soundly cancelled. The task can be `Promise.resolve`d into a Promise<Response> which means it can have multiple subscribers but it will not longer be possible to cancel it. 
 - That task resolves once the headers return from the consumer - it resolves with a Response object. That response object is an Observable<T> where T is determined by the type of response returned. This allows for progress notifications on the response, partial consumption and all the added benefits of observables - this is like the ES7 async generators proposal and unlike fetch. In addition the response object has a `.json` method for reading it at once into JSON like fetch. The response body being observable means you can also interoperate with websocket and sse APIs easily since they will similarly return this type - since while the request itself is a Task the response body is an observable.
 - Similarly, the Request object can take an `Observable` with upload data - this allows for upload progress events, uploads that are not all at once and so on - again giving the benefit of observables.

All the types are as constrained as possible and in particular more constrained. The above is just a suggestion and I did not spend time scrutinising it - the argument I'm making is for using more than a single async abstraction in the http module and more generally in Angular 2.0 rather than using observables, tasks or promises exclusively.

Please let me know what you think about this,
Benjamin

[1] I make some of these arguments in this RxJS issue but honestly I'm not sure reading it is worth it all.

Sander Elias

unread,
Apr 4, 2015, 10:01:57 AM4/4/15
to angular-...@googlegroups.com

Hi Benjamin,

Have done quite some reading today;) Al lot of intellectual jibber-jabber. Most of the stuff has a point, and if you talk long enough, nobody understands the subject anymore.

Let me state 1 think clearly. I think 100% observables is the way forward. Perhaps an added wrapper to enable promises for the situations where they might be a better solution. Let me show you an 100% working wrapper, that would produce a promise just as it is now with $http.

export function http2Prom(req:Request|string) {
  return new Promise(function (resolve,reject) {
      http2Observable(req)
         .retry(3)  
         .subscribe(resolve,reject)
  })
}

I’m assuming you are going to store this in a singleton. Oh, it does a bit more then the current $http, on failure, it retries 3 times before rejecting the promise.

I like the idea of tasks, but those aren’t even on the agenda for es7. Observables are readily available now. (when those land in ES7 it might take some time to rework from RX to that tough!)

Bottom line, if the lowest part of the http2 library is based on observables, it is easy enough to poly-fill other interfaces. The other way around is an uphill battle!

Regards
Sander

inglor

unread,
Apr 4, 2015, 10:17:19 AM4/4/15
to angular-...@googlegroups.com
Thanks for the feedback Sander!

Just to be clear - I understand and use observables. I've used them in several projects for quite a while - first in Rx and later also in RxJS. 

Having a `.retry` method is orthogonal to the concept of observables - it's possible to do this with promises and the same goes for cancellation. RxJS already has `.toPromise` and assimilates promises so your `new Promise` calls is redundant. This thread wasn't about "let's not use observables" it was about "let's use abstractions where they are appropriate". Returning a more constrained type is super useful - reading a remote resource as JSON is just a single value, it's not "possible 0 or more values over possibly some time or no time at all, possibly cold or not with cancellation possibly working" - it is a single value - returning an observable there and more generally returning a much less constrained type is in general a really poor API choice in my humble opinion.

We can benefit a lot from mixing and matching different async primitives - just like `Array#forEach` is super useful but we do `console.log(3)` and not `[3].forEach(function(x){ console.log(x); })`. Different parts of the HTTP wrapper fit in different abstractions - asynchronous abstractions is a topic that has been researched for many years by really smart people, I do not think we should hammer the whole API to use promises or observables - rather we could use the entire async toolbox using promises tasks and observables where appropriate - for representing a single or multiple value with different constraints. 

Bottom line, if the lowest part of the http2 library is based on observables, it is easy enough to poly-fill other interfaces. The other way around is an uphill battle!

I completely agree - I'm not suggesting basing the bottom part on promises either - I think we both agree that using promises all around introduces problems - a lot of them we can find in $http in Angular 1.x and in fetch. To be clear - I am not suggesting this, I'm suggesting using promises for singular values with multiple broadcast, tasks for singular values with single subscriber, observables for multiple values with multiple broadcast and so on. The diagram isn't really "Value - Promise, Iterable - Observable" - it's really more like:

Interface
ValueValueSingularSpatial
GetterGetterSingularSpatial
SetterSetterSingularSpatial
ArrayValuePluralSpatial
IteratorGetterPluralSpatial
GeneratorSetterPluralSpatial
DeferredValueSingularTemporal
PromiseGetterSingularTemporal
ResolverSetterSingularTemporal
StreamValuePluralTemporal
ReaderGetterPluralTemporal
WriterSetterPluralTemporal
(Copied from https://github.com/kriskowal/gtor MIT license - that page again is worth reading). 

While we can map singular values with containers for multiple values it doesn't mean we should always do so, and retry/cancellation is should at least be attempted to solve in a sound way that does not have the interference problem. 

Also, please, I've drank and have been drinking a lot of the observable coolaid myself over the past few years - I use them internally a lot at work, and we have RxJS at our public facing client app and Rx in the backend (heck, we even have RxJava somewhere). So while I appreciate people explaining how valuable observables are - I think it's really just creating clutter in the discussion since I assume most of the people in this group already understand them pretty well. We all agree that observables are nice - I don't understand why they should be used to model everything (rather than just things that are well... multiple values)

Benjamin Lesh

unread,
Apr 4, 2015, 11:03:40 AM4/4/15
to inglor, angular-...@googlegroups.com

promises are a read only view to a future.

If you were to create a new Promise type that was still a spec-compliant Promise, but allowed cancellation from the promise instance, it would have to cancel the entire promise chain. If that chain were forked at any point, you'll cancel the forks too.

There is no way around this. At the point to do code your way around this, you'll have created a single value observable, svc I doubt that will be spec compliant.

In essence, cancellable promises introduce shared, mutable state. That's a nightmare.

If someone can produce a spec compliant, cancellable, retriable promise, that doesn't kill the entire chain of promises when cancelled, and isn't really just a single value observable, I'll reconsider.

--
You received this message because you are subscribed to the Google Groups "angular-data-dev" group.
To unsubscribe from this group and stop receiving emails from it, send an email to angular-data-d...@googlegroups.com.
To post to this group, send email to angular-...@googlegroups.com.
To view this discussion on the web visit https://groups.google.com/d/msgid/angular-data-dev/e59d0a8a-fc25-422c-9a29-e410d7f41dd1%40googlegroups.com.
For more options, visit https://groups.google.com/d/optout.

inglor

unread,
Apr 6, 2015, 9:12:03 AM4/6/15
to angular-...@googlegroups.com, ing...@gmail.com
Reposting after sending as a PM by mistake:

Thanks for that.

Observables are a read only view to the future too - only you're reading multiple values.

There were several attempts to create spec compliant promises that allow cancellation. For example bluebird promises allow cancellation and the feature is used heavily and successfully in complex large projects like WhatsApp Web. That said, I don't like bluebird 2.x's particular attempt - mainly because allowing multiple subscribers causes the interference problem (which is also apparent in observable cancellation which is again - not sound and problematic), all this and more is written in the references I've provided above and the points are from my original post. This area is actually quite researched - I personally learned a lot from reading existing research.

The problem with cancellable promises is as problematic and as apparent in observable cancellation - only not as researched. The problem is with managing a dependency tree which creates user-surprising scenarios. As I point out in the message above and as referenced - this is solvable by using a single subscriber model - like a Task. People did create some - you can look at KrisKowal's gtor (referenced above) and jhusain also mentioned he created one. Also note that observables are not really specified - just implemented, so claiming a model isn't specified isn't really helpful.

Of course it goes without saying that all the other before-mentioned benefits of a multiple primitive solution from an API perspective still stand :)

To sum it up - a more powerful type is a bad thing in API design. a multi-primitive API is something that'll benefit all of us, helping users by giving them constrained types and having the power of observables, promises and tasks where each fits soundly.

Benjamin Lesh

unread,
Apr 6, 2015, 12:09:24 PM4/6/15
to inglor, angular-...@googlegroups.com

Observables are not JUST read only views to a future, though. They can actually create those futures. In fact, they could even return different values depending on when you subscribe to them. For example:

Observable.create(o => {
  o.onNext(Date.now());
});

They're just a bit more flexible and robust as an abstraction than promises. For a wide variety of reasons.

You've raised some really good point, but with respect: Given that this effort has embraced observable with no intention of changing, and it doesn't seem likely we're going to agree on the color of the shed, I think I'm going to bow out of this thread for now.

inglor

unread,
Apr 6, 2015, 1:43:05 PM4/6/15
to angular-...@googlegroups.com, ing...@gmail.com
On Monday, April 6, 2015 at 7:09:24 PM UTC+3, blesh wrote:

Observables are not JUST read only views to a future, though. They can actually create those futures. 

Observables are JUST a read only view to the future - you're consuming an iterable, retry logic for example is "consume more items from the iterable until you've obtained enough non-error items from the future". This is a fundamental property of observables and part of what makes them useful.

Observable.create(o => {
>  o.onNext(Date.now());
> });

That's just random state, I hope you don't _actually_ create observables in a way that depends on when they were subscribed to - and I certainly hope Angular doesn't that's really problematic API design.

You've raised some really good point, but with respect: Given that this effort has embraced observable with no intention of changing, and it doesn't seem likely we're going to agree on the color of the shed, I think I'm going to bow out of this thread for now.

You are of course welcome to bow out of the thread - but this is not the bikeshed, this is the nuclear reactor. Choosing what fundamental abstractions we're going to use for the core API is not a personal preference issue. It is a fundamental property of the API which we only get one chance to pick. I think we all agree observables should be a part of the API, there is no doubt they are more expressive than promises and can represent more scenarios (for which I use them in my own backend and frontend code) however, It's important to offer an API as constrained as possible - for the same reason we wouldn't return "any" from the HTTP service or return an array from Math.abs or use Array#forEach to print a single number. An API based on multiple abstractions will make developers' lives much easier and make Angular 2.0 slightly less alienating. 


Benjamin Lesh

unread,
Apr 6, 2015, 4:38:57 PM4/6/15
to inglor, angular-...@googlegroups.com
> That's just random state, I hope you don't _actually_ create observables in a way that depends on when they were subscribed to

I'm trying to demonstrate a behavior, not get nitpicked for creating a weird API. You can't think about Observables in terms of Promises. You just can't. They'll always depend on when they're subscribed to, because it's the act of subscription that triggers the creation of their future.

I'll change my example to show the current http API:


var currentTime = http('http://sample.com/getCurrentTime.php');
// at this point, no request as been made

currentTime.subscribe(x => console.log(x));
// NOW you're making the AJAX request to get the current time.
// later logs 10:30:00AM

setTimeout(() => {
  currentTime.subscribe(x => console.log(x));
  // AGAIN! you're making the AJAX request to get the current time.
  // later logs 10:30:05AM (in a vacuum, anyhow)
}, 5000);

As you can see, *subscribe* is what triggers the sending of the ajax request. Now if were were to do this with a Promise:

var currentTime = http('http://sample.com/getCurrentTime.php');
// at this point, the AJAX request as been made already.

currentTime.then(x => console.log(x));
// later logs 10:30:00AM

setTimeout(() => {
  currentTime.then(x => console.log(x));
  // later logs 10:30:00AM again, because it's the same value from the same request
}, 5000);


Now imagine that promise was received from a source that you could no longer call, and you wanted to cancel or retry it. You can't.

Angular 2 allows you to inject whatever you like, and you can inject promise-based http abstractions all you like if you strongly feel Promises are better. Or if you like, you can simply use `.toPromise()` and convert it to whatever promise impl you like:  `myObservable.toPromise(RSVP.Promise)`.

It is *my opinion* that Promises are an inferior abstraction to Observables for this use case in a number of demonstrable ways.

I feel just as strongly I won't be able to convince everyone that I'm correct. I'm bowing out because I don't think we're making any progress trying to come to a decision that has already been made. Made, incidentally, by having a similar conversation from which I'm parroting conclusions and thoughts. I've had this conversation dozens of times now at meetups, get togethers, bars, etc. So hopefully someone else will take up my slack. Sorry to be a killjoy, I know debating this stuff can be fun. ¯\_(ツ)_/¯




--
You received this message because you are subscribed to the Google Groups "angular-data-dev" group.
To unsubscribe from this group and stop receiving emails from it, send an email to angular-data-d...@googlegroups.com.
To post to this group, send email to angular-...@googlegroups.com.

Sander Elias

unread,
Apr 8, 2015, 10:40:18 AM4/8/15
to angular-...@googlegroups.com

Benjamin, Ben,

Shouldn't everything be an observable?

The short answer, Yes. At least in the context of the new NG2 communication layer. I’m glad that it is already decided on this.
Besides what Ben already stated, and what I strongly second, observables enable API simplicity. One can simple use composition to get rid of interceptors.
Developers code becomes much simpler, and at the same time much more powerful.
Let me give you a sample that describes behavior. It is not real code, although it will work if you fill in the blanks.

@inject(userObservable,http,req,userIsAllowed)
function getDataFromAuthuserWith3Retrys(req) {
   var result = userObservable()
      .filter(userIsAllowed)  // check permissions before firing off an request.
      .http(req)                   // set up the request, for when I need it
      .map( (x) => { log(x); return x} // log every request, even failed ones
      .retry(3)                     //  Flaky server/connection, and I really need this data
   return result
}

@inject(getDataFromAuthuserWith3Retrys)
function name2UpperReq(req) {
    return getDataFromAuthuserWith3Retrys(req).map((x) =>{ x.name.toUpperCase(), return x}
}

@inject(name2UpperReq)
function someUsersService() {
   name2UpperReq.subscribe(updateModel,handleError)

   function updateModel(response) {
      ...
   }
}

I know that there is a lot to improve in this sample, but I really think that if you want to do the same thing using promises, your code becomes larger, and more complex as this. And this is for a simple http json request, not even for a multiple provider.

Regards
Sander

Jafar Husain

unread,
Apr 8, 2015, 11:04:32 AM4/8/15
to Sander Elias, angular-...@googlegroups.com
Do we need a scalar asynchronous primitive like Task and Promise? Here's an interesting data point: jQuery. Remember that jQuery got by with a collection-based API, which developers regularly used even when retrieving a single value by ID. I think that this example is pretty strong evidence that the average developer will not have difficulty getting their head around an entirely vector-based approach to asynchronous values. 

JH
--
You received this message because you are subscribed to the Google Groups "angular-data-dev" group.
To unsubscribe from this group and stop receiving emails from it, send an email to angular-data-d...@googlegroups.com.
To post to this group, send email to angular-...@googlegroups.com.

wardb

unread,
Apr 8, 2015, 4:00:21 PM4/8/15
to angular-...@googlegroups.com, sande...@gmail.com
Folks let's not forget that 99.9% of average usage is not that complicated. No retry. No cancellation. Just ask the server for something and await a response. 

And cancellation w/ promises is already well understood as one of the flavors of promise rejection. That's how jQuery.ajax does it. That's how $http does it in v.1.

I love the observables. And if you want to get rid of interceptors, that's cool too. But we need a dead simple approach for the usual expectation that a request will be fulfilled once with either a successful result or a rejection+reason.  A `toPromise` feature sounds good to me.

Jafar Husain

unread,
Apr 8, 2015, 4:14:12 PM4/8/15
to wardb, angular-...@googlegroups.com, Sander Elias
Is the 99.9% case no retry? Disagree.  You should probably be retrying any network request to your service layer. Intermittent failures are a thing. 

Just because you can cancel promises with rejection doesn't make it a great idea. :-( Canceling requests is very common in user-interfaces, and could (and probably should) occur every several time if form is closed. Why should we use error handling paths to manage control flow? WHATWG and library authors are not using promises for network requests because they are the optimal solution, but because they are there. Again, I'm for copying good designs.

Angular should do the right thing and take on the learning curve secure in the knowledge that on the other side of that curve developers will be in a better place. That's pretty much the story of Angular 2.0 in a nutshell.

--
You received this message because you are subscribed to the Google Groups "angular-data-dev" group.
To unsubscribe from this group and stop receiving emails from it, send an email to angular-data-d...@googlegroups.com.
To post to this group, send email to angular-...@googlegroups.com.

wardb

unread,
Apr 8, 2015, 6:42:24 PM4/8/15
to angular-...@googlegroups.com, ward...@gmail.com, sande...@gmail.com
You may be describing the practice you recommend. I might even agree with you.

That isn't what most of the world is doing. I don't know of any mainstream Angular tutorials, produced by the Angular team or anyone else, that feature a retry. We have numerous customers with Angular apps; to my knowledge,  none of them issue a retry.

I hasten to add that all of the apps I've referenced target desktop browser usage where the probability of connectivity is very high and the likelihood  is near zero that a retry would do any good at all when the connection fails. 

>That reminds me: we'd be wise to create retry logic that only bothers to retry for known appropriate causes (e.g., timeout and lost connectivity), not for every cause. 

I don't agree that canceling pending requests should be a required side-effect of view navigation (although that may be appropriate in some cases). Cancellation has no effect on what the server does and whether it is beneficial on the client-side is entirely application specific. For example, if I have a data service that maintains an entity cache and I issue a save request, I want that request to finish (if possible) regardless of where the user goes. I may need the response to update the cache (e.g, the store-generated primary keys for new records). Cancellation could leave the cache in a corrupt state.

>Yes I should deal with that possibility anyway. But at least I'm not forcing cache recovery on myself through blind cancellation.

I agree that handling cancellation on the rejection path is not a *great* idea. But it's not a *terrible* idea either, it works, and it's a common practice. 

Finally, I reiterate that most of these requests ... these push requests ... will only ever have a single response. It's another mental hurdle for the developer to have to code for multiple responses when that isn't going to happen.

Sure we should be teaching what WE think are "better" practices. But you may recall the outrage when it seemed that Ng2 and the Ng team were only interested in foisting its notion of "a better way" on the community. They were too cavalier about the concerns of existing Ng developers with substantial existing investments in Ng.  We want to encourage people to port their existing ng v.1 apps to ng2. We succeed in part when we impose as few changes as necessary and provide plenty of bridges. There are obstacles enough already; we don't need more of them. 

I repeat to be clear. I'm in favor of observables. I'm in favor of teaching the advantages of observables. But I'm reminding us of our responsibility to the community as it is today and urging a modicum of humility along the way.

End of rant. :-)

Benjamin Lesh

unread,
Apr 8, 2015, 7:09:46 PM4/8/15
to Jafar Husain, wardb, angular-...@googlegroups.com, Sander Elias
 99.9% of average usage 

The first use case that pops into my head is fairly common... auto completed search text boxes. The classic scenario where you're trottling key presses, getting the value of an input, then sending it to a server to get a list of possible matches to display under the text box. This can often send dozens or even hundreds of Ajax requests, many of which are thrown out. They could be cancelled very, very simply with Observable.prototype.flatMapLatest().  That means that you've saved yourself from a whole love of unnecessary code path execution. With a promise, that `success` path will be called whether you want it to be or not. In an environment where you might already be pushing a lot out of your browser (say a really crappy mobile browser, or even just a very rich UI on a desktop), you don't want to call any code paths you're not using or don't care about.

The *really* nice thing about Observable is generally, as long as your composing correctly, it's just going to handle aborting the HTTP request for you. 


In pseudo-code:

Observable.fromEvent(input, 'keypress').
  throttle(300).map(() => input.value).
  flatMapLatest(q => http('http://search/endpoint?q=' + q)).
  subscribe(showResults);

In the above example, if for type and then pause for 300ms, it sends the request. If I then start typing again and pause for another 300ms it sends a new request, *AND* that first request hasn't arrived yet, it will cancel that first request and wait for the next one to arrive.



Sander Elias

unread,
Apr 8, 2015, 10:43:46 PM4/8/15
to angular-...@googlegroups.com, sande...@gmail.com
Hi Ward,

Folks let's not forget that 99.9% of average usage is not that complicated. No retry. No cancellation. Just ask the server for something and await a response. 
Surerely 99.9% of the is done in NG1, that offers those things easily... how?
 

And cancellation w/ promises is already well understood as one of the flavors of promise rejection. That's how jQuery.ajax does it. That's how $http does it in v.1.
That does make it right, right? Developers tend to use whats available, that's right. But because we did it in the past, should that be an reason not to offer a better option.?
 
I love the observables. And if you want to get rid of interceptors, that's cool too. But we need a dead simple approach for the usual expectation that a request will be fulfilled once with either a successful result or a rejection+reason.
Hmm, I dare to state that 99.9% of the interceptors exist because of the promise system in $http. A sure sign that there is the need for some more control over cancellation [and|or] the need to modify [requests|responses] before they hit base.
 
 A `toPromise` feature sounds good to me.
I agree on this, there are surely use-cases where those will come in handy. And we can do that as a last step in the composition chain. However, we should never default to this! For reasons that are discussed already, but let me restate the in my eyes most important ones.
  1. promises are (in the communication setting) less powerful as observables
  2. they use exactly the same semantics:
       promise.then(successFn,errorFn)
       observable.subscribe(successFn, errorFn, DoneWithoutFailureFn)
  3. given point 2, there is no learning curve for migration of NG1 $http 
Regards
Sander


Sander Elias

unread,
Apr 8, 2015, 11:27:30 PM4/8/15
to angular-...@googlegroups.com, ward...@gmail.com, sande...@gmail.com
Hi Ward

That isn't what most of the world is doing. I don't know of any mainstream Angular tutorials, produced by the Angular team or anyone else, that feature a retry. We have numerous customers with Angular apps; to my knowledge,  none of them issue a retry.

Sure, as NG1 does not offer them an easy option to do a retry, most developers tend to ignore the idea that communications might fail. Does that mean we should not offer a better option?
 

I hasten to add that all of the apps I've referenced target desktop browser usage where the probability of connectivity is very high and the likelihood  is near zero that a retry would do any good at all when the connection fails. 

Even in there you should take care of it. How often does support give the advice: "did you try a reload?" It is even worse, our end-users know this, and do that often before calling support. My bet, in at least 50% of those cases, an ajax request timed out!
 
>That reminds me: we'd be wise to create retry logic that only bothers to retry for known appropriate causes (e.g., timeout and lost connectivity), not for every cause. 

I surely hope so :-) 
 
I don't agree that canceling pending requests should be a required side-effect of view navigation (although that may be appropriate in some cases). Cancellation has no effect on what the server does and whether it is beneficial on the client-side is entirely application specific. For example, if I have a data service that maintains an entity cache and I issue a save request, I want that request to finish (if possible) regardless of where the user goes. I may need the response to update the cache (e.g, the store-generated primary keys for new records). Cancellation could leave the cache in a corrupt state.

Well, it should cancel all pending get requests, unless I specify otherwise. All other request should follow their way.
However, that leaves me wondering, what happens if a [put|post] fails? I do not allow users to navigate away if there might be an issue like this.
 
I agree that handling cancellation on the rejection path is not a *great* idea. But it's not a *terrible* idea either, it works, and it's a common practice. 

A cancellation is not an error! That promise based system force you to threat it like one makes it common practice. NG1 works! that was not a *terrible* idea either. See where I'm going with this?
I still learn every day, and I try not to repeat my mistakes. Lets do better! 
 
Finally, I reiterate that most of these requests ... these push requests ... will only ever have a single response. It's another mental hurdle for the developer to have to code for multiple responses when that isn't going to happen.

Euhm, as I said in my previous message, there in no difference in the $http promise interface as with the observables interface other then that the word .then gets replaced by .subscribe As an normal http-request will fire only once and then be done, this is a non-existing hurdle.  
 
Sure we should be teaching what WE think are "better" practices. But you may recall the outrage when it seemed that Ng2 and the Ng team were only interested in foisting its notion of "a better way" on the community. They were too cavalier about the concerns of existing Ng developers with substantial existing investments in Ng.  We want to encourage people to port their existing ng v.1 apps to ng2. We succeed in part when we impose as few changes as necessary and provide plenty of bridges. There are obstacles enough already; we don't need more of them. 

The thing that gets build will make it drop-dead easy to aid existing stuff. You need an 1 line function to mimic $http for 99.9% of all current use cases ;)
@inject(httpObservable)
function(url){
 
return httpObservable(url).toPromise()
}

Well, there need to be some code added for the config stuff and so on, but that is not that hard to  imagine right?
 
I repeat to be clear. I'm in favor of observables. I'm in favor of teaching the advantages of observables. But I'm reminding us of our responsibility to the community as it is today and urging a modicum of humility along the way.

Sure, but that is not a reason to dumb down the base design, and hand out less powerful abstractions. Certainly not, if those can be easily bolted on, as an separate 'migration' library.
 
End of rant. :-)
I second that. :) 

Oh, and let me repeat that I do like promises, and I'm even in favor of them. Just not in this setting :-P

Regards
Sander
 

Benjamin Gruenbaum

unread,
Apr 9, 2015, 3:38:57 AM4/9/15
to angular-...@googlegroups.com, ward...@gmail.com, sande...@gmail.com
Thanks everyone for the good points. I'll reply to them:

jhusain:
>  Remember that jQuery got by with a collection-based API, which developers regularly used even when retrieving a single value by ID. I think that this example is pretty strong evidence that the average developer will not have difficulty getting their head around an entirely vector-based approach to asynchronous values. 

That's actually a point against observables. jQuery took a bad abstraction for selections (a live List) and instead worked with a useful abstraction which was conceptually representative better - a selection _set_. Developers did not have difficulty getting their head around it because it was inherently _simpler_. This is definitely not the case here imo.

Sander:
> Example with expressive API

That API is nice because of RxJS, not because of the observable abstraction. That's just having a lot of useful methods on the API. If you check out promise libraries a lot of them have a lot of that. Things like retry should also arguably be part of the $http core itself. I'm not sure the best way is to 'withdraw more things'.

blesh:
Now imagine that promise was received from a source that you could no longer call, and you wanted to cancel or retry it. You can't.

That gives you the ability to separate capabilities. You can give someone the ability to cancel or retry your API, or you can decide you don't want them to have that ability. That's a good thing.

Angular 2 allows you to inject whatever you like, and you can inject promise-based http abstractions all you like if you strongly feel Promises are better. Or if you like, you can simply use `.toPromise()` and convert it to whatever promise impl you like:  `myObservable.toPromise(RSVP.Promise)`.

The problem is `.toPromise` does the wrong thing (TM) most of the time since it gets the last value and not all the values from the observable. Also from the same logic Angular 2.0 lets you import RxJS and consume promises with it just fine. 


That said, here are some points for an observable-centric API:

Even if we mix primitives:

 - Promises represent already started operations and are thus eager - observables are mostly lazy. While this isn't an inherent part of promises, in practice promise libraries do this. I like laziness better - what do you think users will enjoy more?
 - Promises are immutable and can be consumed at any point - observables are (mostly) consumed once which I find nicer for this API and it also makes cancellation simpler since people don't expect subscription that's late to always work. 

Sander Elias

unread,
Apr 9, 2015, 5:21:26 AM4/9/15
to angular-...@googlegroups.com, ward...@gmail.com, sande...@gmail.com

Benjamin:

jhusain:
>  Remember that jQuery got by with a collection-based API, which developers regularly used even when retrieving a single value by ID. I think that this example is pretty strong evidence that the average developer will not have difficulty getting their head around an entirely vector-based approach to asynchronous values. 

That's actually a point against observables. jQuery took a bad abstraction for selections (a live List) and instead worked with a useful abstraction which was conceptually representative better - a selection _set_. Developers did not have difficulty getting their head around it because it was inherently _simpler_. This is definitely not the case here imo.

No, the point was, that it’s easy to convert from single results (document.getElementById('someId')) , to result sets ($('#someId')). And people had no problem adapting it. I think this is a fair comparison.


Sander:
> Example with expressive API

That API is nice because of RxJS, not because of the observable abstraction. That's just having a lot of useful methods on the API. If you check out promise libraries a lot of them have a lot of that. Things like retry should also arguably be part of the $http core itself. I'm not sure the best way is to 'withdraw more things'.

That api is only possible because of the observables. Those are the key factor to enable this. If those where not provided by RX, it’s not that hard to provide them yourself. Show me how to do the same thing with promises, with the same amount of code. BTW, I used filter and map, I’m pretty sure those will be provided by default in ES7. And that’s it, the rest is just composition, enabled by observables.

blesh:
Now imagine that promise was received from a source that you could no longer call, and you wanted to cancel or retry it. You can't.

That gives you the ability to separate capabilities. You can give someone the ability to cancel or retry your API, or you can decide you don't want them to have that ability. That's a good thing.

Show me some code that can do this with promises, and still is as terse and concise as the observables solution. Oh, and if you want, you can take the ability to cancel or retry away with observables too. Even when it is a bad idea to begin with. If you really want, you can enforce this separation. That does not make it a good idea for an communication layer!

Angular 2 allows you to inject whatever you like, and you can inject promise-based http abstractions all you like if you strongly feel Promises are better. Or if you like, you can simply use `.toPromise()` and convert it to whatever promise impl you like:  `myObservable.toPromise(RSVP.Promise)`.

The problem is `.toPromise` does the wrong thing (TM) most of the time since it gets the last value and not all the values from the observable. Also from the same logic Angular 2.0 lets you import RxJS and consume promises with it just fine. 

If I recall correctly you proclaimed that simple http calls should yield a promise. Well, simple http calls will allways yield to a single result (or none) so wrapping this single result to a promise does not comply how?

Even if we mix primitives:

 - Promises represent already started operations and are thus eager - observables are mostly lazy. While this isn't an inherent part of promises, in practice promise libraries do this. I like laziness better - what do you think users will enjoy more?

Ok, so for promises we should use a library to enable laziness, and for observables we only can’t use a library(RX as you noted above!) otherwise we make the API to easy? Did I get that right :-P
On a serious note, laziness is the way to go if you want to keep your performance high.

 - Promises are immutable and can be consumed at any point - observables are (mostly) consumed once which I find nicer for this API and it also makes cancellation simpler since people don't expect subscription that's late to always work. 

Promises have their place. But in communication settings observables make just a way better API. A part of that API should be, return a promise. For those things you want/need to cache/memorize.

Regards
Sander

Benjamin Lesh

unread,
Apr 9, 2015, 6:14:48 AM4/9/15
to Sander Elias, wardb, angular-...@googlegroups.com

We could end this whole discussion by aliasing `.subscribe()` as `.then()` and telling the promise diehards they won.

Sander Elias

unread,
Apr 9, 2015, 7:02:50 AM4/9/15
to angular-...@googlegroups.com, sande...@gmail.com, ward...@gmail.com
@blesh

We could end this whole discussion by aliasing `.subscribe()` as `.then()` and telling the promise diehards they won.

You made me laugh out loud :) Mainly because it is so true! 

Regards
Sander 

Jeff Cross

unread,
Apr 21, 2015, 1:45:23 PM4/21/15
to angular-...@googlegroups.com
I'm just settling back into the office after being out for a couple of weeks. 

Thanks for opening this thread, Benjamin! There's been a lot of good, rational discussion here. I only have a couple of things to add here.

  1. I'm not absolutely opposed to mixing primitives. There is some cost in composition and cognitive overhead in mixing, and this cost needs to be justified.
  2. Does a subscription to a request with the new http library represent a single eventual value? In the majority of use cases, in ideal conditions, probably. In all reasonable use cases, in real-world conditions? No. The subscription may error (yes, promises know about errors, too), or may receive a sequence of values when using short polling for example. In the interest of helping make great applications for end users on varying devices and networks, I suggest we encourage developers to make network calls more resilient.
  3. Regarding statements that some things that are "just as easy" with promises, it'd be more likely to convince others if demonstrated with side-by-side code comparing observable with promise. In my experience, the fact that promises don't facilitate creation or destruction is a big factor in how complex certain tasks are.
  4. For the most basic use cases, where a user just wants to get a value over http and hope for the best, an observable signature is just as simple as a promise. 
  5. The core team is going to take some ownership of educating the community on observables, similar to how we helped in a small way educate on promises. We think there's enough value in composition and overall simplicity that it's worth making this a preferred first-class primitive. We also plan to help move the Observable TC39 spec forward however we can.

Sander Elias

unread,
Apr 23, 2015, 3:49:18 AM4/23/15
to angular-...@googlegroups.com
Hi Jeff,

Thanks for the heads up!
I could not agree more. And I'm really glad with point 5! The education of users on observables, is very much needed. Also I'm glad to hear that the core team is involved with TC39 on this! It's just too often that they come out with something that's just off a few bits.

Regards
Sander

Benjamin Gruenbaum

unread,
May 7, 2015, 5:23:45 AM5/7/15
to angular-...@googlegroups.com
Allow me to address these issues as it looks like this sort of thing isn't going to go away quietly:

>  I'm not absolutely opposed to mixing primitives. There is some cost in composition and cognitive overhead in mixing, and this cost needs to be justified.

Right, I completely agree that we should strive for less overhead. There is no more overhead in mixing promises and observables than there is in mixing numbers and arrays of numbers. They just represent two conceptually different things. One represents a single eventual value and one represents multiple eventual values. Can you always return an array of numbers from an hypothetical `abs`? Yes. Should you? Probably not. Why? Because giving users constrained APIs is a good thing.

Does a subscription to a request with the new http library represent a single eventual value? In the majority of use cases, in ideal conditions, probably. In all reasonable use cases, in real-world conditions? No. The subscription may error (yes, promises know about errors, too), or may receive a sequence of values when using short polling for example. In the interest of helping make great applications for end users on varying devices and networks, I suggest we encourage developers to make network calls more resilient.

In the cases t represents a single eventual value - then a promise is a lot nicer since you _have the guarantee_ on the consuming side.

An HTTP call _cannot_ receive a sequence of values, it receives a single response. Its _body_ can receive multiple values (like in short polling) which is great! We'll be exposing a much clearer type - the request itself is a task/promise but its body can be streamed since it's a construct that allows it (like an observable or async iterator).

Retries can be written as `$http({url: url, retries:3 })` just as easily, cancellation _works_ with promises, it does in Angular 1, it does when WhatsApp built their web client with bluebird, and people have been successfully using it for years. Retries with promises are trivial.

> Regarding statements that some things that are "just as easy" with promises, it'd be more likely to convince others if demonstrated with side-by-side code comparing observable with promise. In my experience, the fact that promises don't facilitate creation or destruction is a big factor in how complex certain tasks are.

The fact there is much less _action at a distance_ when using promises is an incredibly nice property of an API. You're never worried that just consuming the observable you're "creating things". Of course - you do have things that facilitate construction and destruction __functions__. Just like in every other API, __functions__ are what facilitates construction, a function represents the action and not some action-at-a-distance construct. This is what developers expect.

someWebRequest().cancel(); // promise cancellation with most promise libraries
retry(someWebRequest, 3); // promise retries, which can be written as:

```js
function retry(fn, retries){
     return Promise.resolve(retries).then(function repeat(n){
           if(n === 0) throw new Error("Failed with all attempts);
           return fn().catch(repeat.bind(null, n-1);
     });
});
```

This is of course trivial to include as a part of a library - want to know the fun part? It works just fine with regular functions too :)

The fact promises provide a constrained API, have a much simpler transition graph and are _always_ immutable in terms of their value makes for really nice APIs. People trivially cache them, they know exactly what they're getting and it's probably a lot of why while observables exist for as long as promises in JavaScript and until recently have been "as native" - promises are a much much more popular construct. I say that loving Rx, but I have to say we're not a group of target Angular 2.0 users, we're a bunch of people with experience with both Rx and Promises and don't mind the complexity nearly as much as ordinary users.

For the most basic use cases, where a user just wants to get a value over http and hope for the best, an observable signature is just as simple as a promise. 

I beg to differ, just because the signature is similar doesn't mean the contract is. What if the observable is hot? What if the observable returns multiple values? What if the observable is disposed? Do I need to dispose it in my code? What happens when it's completed? 

These are all things that are no-issues with promises, and if you want to convert a promise to an observable (or a promise returning function) it's just as trivial as the other way. Giving users contracts that are more constrained is super important in a project as big as Angular and I think we should strive for it. 

The core team is going to take some ownership of educating the community on observables, similar to how we helped in a small way educate on promises. We think there's enough value in composition and overall simplicity that it's worth making this a preferred first-class primitive. We also plan to help move the Observable TC39 spec forward however we can.

That's nice, but your contribution on educating users on promises was very mild at best. They were already a part of the __biggest and most widely used library__, they were popular on the server already, and people are familiar with them because they exist natively in virtually every other language (C#, Scala, Java 8, Python and so on). Observables aren't "simpler" than promises in any regard (promises do a strict subset of what observables do - they're always eager, always cold, always immutable, always singular and have fewer states). They compose just as well for more scenarios.

Observables aren't only harder for people to grasp (and trust me, I teach both) - they also have a fraction of the fraction of the ecosystem promises have. I love observables like I said before and I use them in my own code - but I think the motivation for using them exclusively in Angular 2.0 is very weak at best. 

As for the TC39 spec - there are two competing proposals, and frankly the other one (async iterators) is backed up by at least as many (if not more) members of the TC at the moment. So we might end up as being the only ones with observables where developers don't understand why they have to deal with the overhead and complexity of a construct different than what the language already offers for the same problem in a few months.

However, if we go with something similar to what I had in mind in the first message on this thread we can more easily adapt later, in 2.1, if the need arises. 

Sorry for the late response, I'm honestly hoping no one (ahem) replies with snark (again) to this, if people want to reply please do and please offer me the same level of respect as I'm offering you as a member of this group. It's already discouraging enough as it sometimes feels like I'm listening to ideas of forcing the spec and personal agenda through the framework in the expense of potential users. 

Benjamin Lesh

unread,
May 7, 2015, 4:46:30 PM5/7/15
to inglor, angular-...@googlegroups.com

> There is no more overhead in mixing promises and observables than there is in mixing numbers and arrays of numbers

When switching between promises and observables, there will be additional object allocations, closures, and next frame executions. Those are indeed more overhead.

Promise cancellation is usually ugly, and there are no standards around them. I've seen token based implementations. Impls that required a call to finally to trigger their start. Impls that require functions to be composed and passed down the promise chain.

Can you retry a promise? Sort of, not really. Not without doing some hacky, sketchy stuff. At that point, it's not a promise, it's a promise with a whole bunch of other code around it.

It's just not what they're designed for. If you need cancellation and retry, use an observable of one.

I'm not saying promises are horrible and you shouldn't use them. But they're a less useful abstraction for many use cases.

If you really like promise, then you can always call toPromise() on the Observable.

--
You received this message because you are subscribed to the Google Groups "angular-data-dev" group.
To unsubscribe from this group and stop receiving emails from it, send an email to angular-data-d...@googlegroups.com.
To post to this group, send email to angular-...@googlegroups.com.

Benjamin Lesh

unread,
May 7, 2015, 4:50:25 PM5/7/15
to inglor, angular-...@googlegroups.com

Iterator of promise is equally a thumbs down. Too many allocations, to slow, less useful. The TC39 is a smart group of people, so I sincerely doubt they'd go that route. I'd be shocked if they did, really.

Jafar Husain

unread,
May 7, 2015, 4:52:59 PM5/7/15
to Benjamin Gruenbaum, Benjamin Lesh, angular-...@googlegroups.com
Benjamin:

Are you concerned about the mental overhead required by the conversion process from an Observable to a Promise? Is there some reason why you don't think this is a satisfactory concession to those who would rather use Promises?

JH

Jafar Husain

unread,
May 7, 2015, 5:00:28 PM5/7/15
to Benjamin Lesh, inglor, angular-...@googlegroups.com
I want to point out that there is support for the iterator<promise> on the committee. It is not clear that this would be a replacement for observable, anymore then streams would be a replacement for observable. These types are not in conflict with each other. Iterator<promise> has certain advantages that observable does not have, like back pressure support for example. It also has several disadvantages like memory overhead and additional scheduling. 

Would you model mouse moves as an iterator<promise>? It would be pretty expensive. There's no reason to believe that these proposals are either/or options. 

JH

Benjamin Gruenbaum

unread,
May 7, 2015, 5:09:37 PM5/7/15
to angular-...@googlegroups.com, ing...@gmail.com
When switching between promises and observables, there will be additional object allocations, closures, and next frame executions. Those are indeed more overhead.

That actually didn't address the point you're replying to (exposing a narrower API). If you're concerned about performance - bluebird promises are at the moment orders of magnitude faster than both RX (and BaconJS) observables and of native promises. In fact I've personally had to guarantee someone just today that using observables is OK in their apps because they were concerned about performance after benchmarking - which is exactly why I don't think performance should even be an argument here - how many observables are we going to have running at the same time, 10? 100? 1000? Let's say we allocate for each one a closure and assimilate a promise into it (although that doesn't require a closure really) - we're still _way_ above a noticeable performance overhead. In C# it works quite nicely (tasks + Rx) and I'm enjoying mixing methods returning promises using current APIs with RxJS with Angular 1.x.

That said, I'm all ears if you have a real world performance scenario where such interop and assimilation would matter - that should definitely be something we should consider. The abstraction costs should be as close to zero as possible. 

Promise cancellation is usually ugly, and there are no standards around them. I've seen token based implementations. Impls that required a call to finally to trigger their start. Impls that require functions to be composed and passed down the promise chain.

Well, as far as I'm aware observables aren't nearly as specified as promises are at an interop level - there is no spec observables abide by at the moment (That for example, RxJS and BaconJS and native obserables all adhere to). That said, there is an old cancellation spec https://github.com/promises-aplus/cancellation-spec and there's cancellation as being worked on for WhatWG fetch. Once that lands it doesn't get more standardized than this.

Inherently, I don't really like promise cancelation though - we don't have to go with promises all around (I wouldn't want that), which is why the cancellable parts weren't promises in my original message here. The fact observables can be "hot" is actually a strong suit for them here in this regard. Companies like whatsapp do use promise cancellation for really complex scenarios and are quite happy with it though - one option is for it to work like observable cancellation.


Can you retry a promise? Sort of, not really. Not without doing some hacky, sketchy stuff. At that point, it's not a promise, it's a promise with a whole bunch of other code around it.

The point of the example I posted is that it retries an arbitrary function, it actually works for an arbitrary value with no modifications - if we drop action-at-a-distance and start writing composable functional code this sort of thing stops being a problem. If that's kind of too much fp (not even going to mention fantasy-land much) and not "JS coding style" we can always put a `.retry` method on $http with promises that's super easy too.

It's just not what they're designed for. If you need cancellation and retry, use an observable of one.

Actually robust network communication is _exactly_ what they were designed for, if I may suggest Mark Miller and Barbara Liskov both have different but very related reading material on this. Promises were created from day one for robust communication. The title of fault-tolerance famed Liskov for the paper is even "Promises: linguistic support for efficient asynchronous procedure calls in distributed systems". Just saying :) 

I'm not saying promises are horrible and you shouldn't use them. But they're a less useful abstraction for many use cases.

I agree, which is why I was not suggesting an overall promise based implementation but a mixture of models. We both like observables for multiple-part things - let's use them there - but can we agree that the API surface of a promise is much smaller and that's a good thing? The fact it's always cold (in a good cached way), always a singular value, never disposed and so on makes it a much simpler type to work with. 

If you really like promise, then you can always call toPromise() on the Observable.

Of course, you can always assimilate a promise, or promise returning function into an observable, just as easily, I do this frequently and for great extent when working with observables and it's been working out great for me. This also goes for the point above - you can return promises (which have a much simpler type surface since they express a much more constrained case) and then assimilate them into observables which you'll have anyway. Creating compasable and robust software.

I'd love to hear your opinion about the rest of my points too. It's not just about API surface, adoption and competing specs - as well as what everyone else is doing. I think I made some strong points and as an Rx fanboy to another I'd love to hear your opinion.

Iterator of promise is equally a thumbs down. Too many allocations, to slow, less useful. The TC39 is a smart group of people, so I sincerely doubt they'd go that route. I'd be shocked if they did, really.

For one thing it's not slow (I tested) - so let's ignore that. I like Observables too, but at the moment there are two competing specs from what I understand. Just because the TC is full of smart people doesn't mean they all agree with me (or you) about observables as a language level construct (let alone language level support like `for... on`). I'd love to see those things in my code which is why I go around bugging people asking them to implement this on Traceur (which is great!). But we really can't be sure.

Benjamin (Inglor) Gruenbaum

unread,
May 7, 2015, 5:22:27 PM5/7/15
to Jafar Husain, Benjamin Lesh, angular-...@googlegroups.com
> I want to point out that there is support for the iterator<promise> on the committee. It is not clear that this would be a replacement for observable, anymore then streams would be a replacement for observable.

Perhaps, I'm all game for both iterator<promise> and Observable - in fact I'll likely use observables a lot more. However, the impression I got from people smarter than me that are more involved in the process is that from the looks of it there is a competition between the proposals. Like you said there is support for iterator<promise>.

I don't think we should drop observables from Angular - in fact before I knew they were in the prototype it was one of the things I was going to suggest we consider myself. I just want to make sure that Jeff and the NG core team understand how big of a project educating the community is - especially on a much less popular and less adopted construct than promises. Personally, I do my share in lectures on Rx (in C#) and Observables in JS - but boy am I getting some confused (not to say angry) faces sometimes (some excited ones too!). 
 
I was just replying to Jeff's points in sequence and it was the last one and I tried to do my best to address all his points.

Benjamin Lesh

unread,
May 7, 2015, 6:02:17 PM5/7/15
to inglor, angular-...@googlegroups.com, Jafar Husain

I guess I can no longer tell what point is trying to be made. What is your position, Benjamin? If you were to "win" this argument, what would be the "win conditions"?

Benjamin (Inglor) Gruenbaum

unread,
May 7, 2015, 6:16:11 PM5/7/15
to Benjamin Lesh, angular-...@googlegroups.com, Jafar Husain
First of all, I don't consider this an argument - I consider this a debate. For that reason, I don't think of getting what I want as "winning" because that would imply someone is "losing", where in practice my goal is to discuss how asynchronous APIs in Angular 2.0 should look like. You guys have raised some really strong points and I think it's safe to say we're all a tiny bit smarter because of it :)

The reason I started this thread is that I don't think mixing primitives (using observables, but using them _with_ promises and tasks) has been properly evaluated for Angular 2.0.

I think that we should seriously consider using multiple asynchronous primitives for how http and other data APIs are going to look in 2.0.

Again, I'm not saying observables are bad, or that they should not be used as part of our API for 2.0. 

However, augmenting my original proposal:

 - Making an http request takes a Request object (or just URL + data) and returns a `Task<Response>`. Since it's a task it can be soundly cancelled. It can also contain a retry method. The task can be `Promise.resolve`d into a Promise<Response> which means it can have multiple subscribers but it will not longer be possible to cancel it. It can also be cast into an Observable in a way that keeps retries and cancellation. Unlike an observable, it's always broadcast, singular, microtask scheduled, and cached. 
 - That task resolves once the headers return from the consumer - just like how it works with callbacks in nodes or in `fetch` - it resolves with a Response object. That response object is an Observable<T> where T is determined by the type of response returned. This allows for progress notifications on the response, partial consumption and all the added benefits of observables - this is like the ES7 async generators proposal and unlike fetch. In addition the response object has a `.json` method for reading it at once into JSON like fetch - this can also just be a `.subscribe`. The response body being observable means you can also interoperate with websocket and sse APIs easily since they will similarly return this type - since while the request itself is a Task the response body is an observable. This also means we have observables for multiple values angular wide for anything multiple.
 - Similarly, the Request object can take an `Observable` with upload data - this allows for upload progress events, uploads that are not all at once and so on.

We should also consider async pull iterators here, since `fetch` is gaining proper cancellation this seems like the major differentiating factor at this point. We can't ignore standards and `fetch` is the web-standard way to do this. Whether we like it or not it's the API people are going to recognize for making web requests in a year or two. That said, while I think it's super important to evaluate it - I'm strongly in favor of observables (at least atm) and not async pull iterators for this use case (Http request/response).

 

wardb

unread,
May 7, 2015, 6:27:16 PM5/7/15
to angular-...@googlegroups.com, jhu...@netflix.com, bl...@netflix.com
+100 to Ben G's reasoning.

I realize that "+100" doesn't add substance. It *does* mean he has company.

I keep hearing many of you say that promises are inadequate most of the time. That flies in the face of a huge number of very successful deployments out there. 

It is blindingly obvious just looking at the body of existing apps in the wild that a single, cold response - a promise response - is exactly what we need most of the time from our client/server HTTP interactions. "Get me the Customer with Id = 42".  That is an unambiguous request for a single response. Once I have it, I have it. I can pass that resolved promise along and its value will be immediately available. I don't risk another turn or an unwanted trip to the server.

This is the bread and butter of application HTTP requests.  I don't see this changing very much for the foreseeable future.  I DO see more push interactions in the near future ... and these are perfect candidates for observables. But HTTP? Seriously?

The retry/cancellation critique is a red herring ... as Ben G. explains. For the record, retry and cancellation with promises is something we've long known how to do  ... when we've needed them. The real problem has been knowing when, why, and what to do when cancelling/retrying. Those are much more difficult issues ... issues that *observables do not solve any better than promises*. What is the program supposed to do when the user cancels? Aborting is not always the right answer. Why bother retrying if the connection is broken? How often do you retry? Do you progressively delay the next retry? These are not questions whose answers turn on whether you use promises or observables.

Promises are far easier to explain than observables .... straight up. If you took people who knew neither, you could be sure they'd learn promises before they got anywhere with observables. This "experiment" has been tried many times already in the real world over the last few years. We know that promises are widely understood today ... especially within the Ng community ... whereas Rx has had pathetic traction.

Let's also get real about Angular's ability to change the world. Angular played a very small (and belated) role in teaching promises.  Angular v.2 may well encourage learning of observables and Rx principles. But we should practice some humility. Let's not exaggerate the Angular Elite's ability to overcome the resistance to Rx when so many others have failed.

Most importantly ... we have to bring the Ng v.1 community with us. ***That community is already pissed off about Ng 2***. We should not blithely add to the discontent by forcing yet another difficult paradigm down their throats.

Finally, I want to reiterate that ... **like Ben G ... I am in favor of observables in Ng. 2.0**.  I am a fan!  I love observables!  Can we just stipulate that?

My argument is that we can't force observables on the Ng community. They're is no valid reason to do so. And they will hate us for it. We can and should offer both.

Jeff Cross

unread,
May 7, 2015, 7:00:00 PM5/7/15
to angular-...@googlegroups.com, ward...@gmail.com, jhu...@netflix.com, bl...@netflix.com
I understand and appreciate that Benjamin's original motivation was to push for generally considering mixing primitives rather than standardizing on one. Good perspectives and ideas have been presented by all. If you have specific APIs within Angular core in which you'd like to discuss the use of observables or other primitives, feel free to open an issue for discussion at angular/angular, as this group is only a tiny subset of the people involved in design decisions in Angular.

Regarding Http, it's a pretty simple library, and just because we're building an official one in core, doesn't mean anyone in the community (including people in this thread) can't create their own alternative implementation. It has no technical preference or advantage over any other third-party http library, just some advertising benefits of being in our official documentation.

I'm locking this topic. I think everything to be said has been said 10 times :). I now want to never use a Promise or Observable again, back to callbacks!
Reply all
Reply to author
Forward
This conversation is locked
You cannot reply and perform actions on locked conversations.
0 new messages