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

Decouple $q in AngularJS 2.0 (and/or switch to bluebird) #6697

Closed
mgcrea opened this issue Mar 15, 2014 · 26 comments
Closed

Decouple $q in AngularJS 2.0 (and/or switch to bluebird) #6697

mgcrea opened this issue Mar 15, 2014 · 26 comments

Comments

@mgcrea
Copy link
Contributor

@mgcrea mgcrea commented Mar 15, 2014

AngularJS currently uses a shim of q. I haven't dug into the actual implementation, but I think it might be interesting to review a possible change for the 2.0 milestone.

I think AngularJS should use an agnostic $Promise service accross the framework, and extract the current $q shim out of the core. Letting user choose their Promise framework. Thanks to the unified promise spec, this should be doable.

Also, I would strongly advise to pick bluebird over q for the future.

I've been doing NodeJS work for a couple of years, and switching from q to the very well-crafted bluebird has been a really pleasant experience. The API is far superior and the performance is unmatched.

Regarding performance, have a look at this article.

But you said promises are slow!
Yes, I know I wrote that. But I was wrong. A month after I wrote the giant comparison of async patterns, Petka Antonov wrote Bluebird. Its a wicked fast promise library, and here are the charts to prove it.

file time(ms) memory(MB)
callbacks-original.js 316 34.97
callbacks-flattened.js 335 35.10
callbacks-catcher.js 355 30.20
promises-bluebird-generator.js 364 41.89
dst-streamline.js 441 46.91
callbacks-deferred-queue.js 455 38.10
callbacks-generator-suspend.js 466 45.20
promises-bluebird.js 512 57.45
thunks-generator-gens.js 517 40.29
thunks-generator-co.js 707 47.95
promises-compose-bluebird.js 710 73.11
callbacks-generator-genny.js 801 67.67
callbacks-async-waterfall.js 989 89.97
promises-bluebird-spawn.js 1227 66.98
promises-kew.js 1578 105.14
dst-stratifiedjs-compiled.js 2341 148.24
rx.js 2369 266.59
promises-when.js 7950 240.11
promises-q-generator.js 21828 702.93
promises-q.js 28262 712.93
@caitp
Copy link
Contributor

@caitp caitp commented Mar 15, 2014

We're probably going to be using ES6 promises in Angular 2.0. I can't promise that the digest cycle will be separated from $q in 1.x, but that's really the big thing that gets in the way of allowing a pluggable interface, as far as I'm aware.

@llambda
Copy link

@llambda llambda commented Mar 15, 2014

+1 a pluggable promise interface would be great

Just a comment on native promises, They are in ES six however they don't have the superb long stacktrace support that bluebird has. Also, bluebird can do 1 million promises per second while native promises and chrome are much slower at 25,000. That might not matter to most people but it may matter to somebody.

@caitp
Copy link
Contributor

@caitp caitp commented Mar 16, 2014

Pluggable would be nice, but for the moment, it's not super feasible, because promises are tied to digest cycle. We can do this better for 2.0, but it might be very hard to remove this glue from 1.x, and that is the major barrier. In addition, it's very easy to mock the promise implementation in angular so that it can be used in tests. With a pluggable system, this is greatly complicated, and these things hurt.

In any case, we can probably use Zone.js in 1.x to enable longer stack traces like Bluebird, even with the native $q implementation. I haven't yet looked at how difficult this would be, but it seems like something which is backportable to 1.x. It's true that we could probably use a better nextTick implementation which would perform better in the browser (like setImmediate, when available), among other things, but the real performance killer of the promise implementation in Angular isn't waiting for the next tick, but performing an app-wide digest. So even with Bluebird, if it's tied to the digest cycle, you wouldn't really see an improvement here for 1,000,000 promises simultaneously.

@llambda
Copy link

@llambda llambda commented Mar 17, 2014

@caitp thanks for the excellent info and I didn't know about Zone.js.

@btford btford added this to the Ice Box milestone Mar 17, 2014
@benjamingr
Copy link
Contributor

@benjamingr benjamingr commented Apr 24, 2014

+1 for decoupling it. I'd love to use a stronger promise implementation.

@probablyup
Copy link

@probablyup probablyup commented Apr 25, 2014

@llambda "Also, bluebird can do 1 million promises per second while native promises and chrome are much slower at 25,000." That's crazy! I'd expect native browser methods to be an order of magnitude faster. Does that data come from Bluebird's page or somewhere else?

@benjamingr
Copy link
Contributor

@benjamingr benjamingr commented Apr 25, 2014

It comes from the fact the guy writing Bluebird has very intimate knowledge of JS engines :) Also, JS isn't slower than native when you understand the JIT in these cases.

@caitp
Copy link
Contributor

@caitp caitp commented Apr 25, 2014

V8's promises are implemented (mostly) in JS, anyways, so it's true that there is very little difference in these cases.

@benjamingr
Copy link
Contributor

@benjamingr benjamingr commented Apr 29, 2014

To clarify, I'm against including Bluebird in Angular, I'm for decoupling the architecture here, and allowing users to easily hook their own implementation.

@petkaantonov
Copy link

@petkaantonov petkaantonov commented May 11, 2014

I'd expect native browser methods to be an order of magnitude faster.

Built-ins need to adhere to ridiculous semantic complexity which only gets worse as more features get added into the language. The spec is ruthless in that it doesn't leave any case as "undefined behavior" - what happens when you use splice on an array that has an indexed getter that calls Object.observe on the array while the splice is looping?

If you implemented your own splice, then you probably wouldn't even think of supporting holed arrays, observable arrays, arrays with funky setters/getters and so on. Your splice would not behave well in these cases but that's ok because you can just document that. Additionally, since you pretty much never need the return value of splice, you can just not return anything instead of allocating a wasted array every time (you could also make this controllable from a parameter if needed).

Already with the above, you could probably reach the perf of "native splice" without even considering the fact that user code is actually optimized and compiled into native code. And when you consider that, you are way past any "native" except when it comes to special snowflakes like charCodeAt, the math functions and such.

Thirdly, built-ins are not magic that avoid doing any work, they are normal code implemented by humans. This is biggest reason for the perf difference specifically in the promise case - bluebird is extremely carefully optimized and tuned to V8 optimizing compiler expectations whereas the V8 promise implementation is looking like it's directly translated from the spec pseudo-code, as in there is no optimization effort at all.

@jdalton has been trying to debunk the "natives are fast" myth for years and might have additional points.

@jdalton
Copy link
Contributor

@jdalton jdalton commented May 11, 2014

👍 for allowing the devs choice. To address @petkaantonov point, he's right emerging native API's rarely have a focus on performance as implementers are more concerned with simply getting the API working correctly. Native API is no guarantee for performance. Making those calls requires profiling and weighing the pros/cons of native vs. custom implementations.

@jonathanrdelgado
Copy link

@jonathanrdelgado jonathanrdelgado commented Sep 16, 2014

+1 for pluggable promises!

@patbaker82
Copy link

@patbaker82 patbaker82 commented Oct 6, 2014

+1

1 similar comment
@mlegenhausen
Copy link

@mlegenhausen mlegenhausen commented Oct 10, 2014

+1

@pilwon
Copy link

@pilwon pilwon commented Oct 12, 2014

👍 👍 👍 👍 👍 👍 👍 👍 👍 👍

@yaru22
Copy link

@yaru22 yaru22 commented Oct 12, 2014

+1

1 similar comment
@manland
Copy link

@manland manland commented Oct 12, 2014

+1

@lgalfaso
Copy link
Member

@lgalfaso lgalfaso commented Jan 7, 2015

Angular 2.0 has its own repository and is not going to use $q nor reimplement their own version of Promise/A+. I think this can be closed now

@metamatt
Copy link
Contributor

@metamatt metamatt commented Jun 11, 2015

BTW it turns out to be quite easy to use Bluebird with Angular 1.x; Bluebird has a setScheduler hook that supports exactly this (example via StackOverflow). I just started using this and am impressed at how easy it was to drop in and how big the benefits are.

@benjamingr
Copy link
Contributor

@benjamingr benjamingr commented Jun 11, 2015

@metamatt actually more like http://plnkr.co/edit/ggMzDwnkzMOc0MccdVEu?p=preview in order to really decouple $q (this does the same as your example - only it replaces all the $q promises internally too).

@metamatt
Copy link
Contributor

@metamatt metamatt commented Jun 11, 2015

@benjamingr oh yes even better. Thanks for educating everyone on this.

@metamatt
Copy link
Contributor

@metamatt metamatt commented Jun 11, 2015

Hmm. I tried (a riff on) that and found it does indeed work, but there are a bunch of places both in my own code (mostly related to $timeout and $timeout.cancel()) and library code I don't control (ui-router) that were assuming it's fine to not handle promise rejection. That's a silent problem in $q, and a noisy one in Bluebird, and that noise is one of the benefits of using Bluebird, but it's got a nonzero switching cost for an existing codebase. More info here.

@sariyu
Copy link

@sariyu sariyu commented Oct 1, 2015

+1

1 similar comment
@lifenautjoe
Copy link

@lifenautjoe lifenautjoe commented Oct 13, 2015

+1

@yoeen
Copy link

@yoeen yoeen commented Dec 18, 2015

+1

@lgalfaso
Copy link
Member

@lgalfaso lgalfaso commented Dec 18, 2015

#12533 (comment) provides a way to replace $q with bluebird. All the necessary pieces will be part of 1.5. On top, Angular 2 uses native Promises, so there is nothing else actionable. For these reasons I am locking this issue

@angular angular locked and limited conversation to collaborators Dec 18, 2015
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Projects
None yet
Linked pull requests

Successfully merging a pull request may close this issue.

None yet
You can’t perform that action at this time.