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

Comments

@mgcrea
Contributor

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

This comment has been minimized.

Show comment
Hide comment
@caitp

caitp Mar 15, 2014

Contributor

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.

Contributor

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

This comment has been minimized.

Show comment
Hide comment
@llambda

llambda 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.

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

This comment has been minimized.

Show comment
Hide comment
@caitp

caitp Mar 16, 2014

Contributor

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.

Contributor

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

This comment has been minimized.

Show comment
Hide comment
@llambda

llambda Mar 17, 2014

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

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

This comment has been minimized.

Show comment
Hide comment
@benjamingr

benjamingr Apr 24, 2014

Contributor

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

Contributor

benjamingr commented Apr 24, 2014

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

@probablyup

This comment has been minimized.

Show comment
Hide comment
@probablyup

probablyup 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?

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

This comment has been minimized.

Show comment
Hide comment
@benjamingr

benjamingr Apr 25, 2014

Contributor

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.

Contributor

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

This comment has been minimized.

Show comment
Hide comment
@caitp

caitp Apr 25, 2014

Contributor

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

Contributor

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

This comment has been minimized.

Show comment
Hide comment
@benjamingr

benjamingr Apr 29, 2014

Contributor

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.

Contributor

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

This comment has been minimized.

Show comment
Hide comment
@petkaantonov

petkaantonov 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.

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

This comment has been minimized.

Show comment
Hide comment
@jdalton

jdalton May 11, 2014

Contributor

👍 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.

Contributor

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.

@jonathandelgado

This comment has been minimized.

Show comment
Hide comment
@jonathandelgado

jonathandelgado Sep 16, 2014

+1 for pluggable promises!

jonathandelgado commented Sep 16, 2014

+1 for pluggable promises!

@patbaker82

This comment has been minimized.

Show comment
Hide comment
@patbaker82

patbaker82 commented Oct 6, 2014

+1

@mlegenhausen

This comment has been minimized.

Show comment
Hide comment
@mlegenhausen

mlegenhausen commented Oct 10, 2014

+1

@pilwon

This comment has been minimized.

Show comment
Hide comment
@pilwon

pilwon Oct 12, 2014

👍 👍 👍 👍 👍 👍 👍 👍 👍 👍

pilwon commented Oct 12, 2014

👍 👍 👍 👍 👍 👍 👍 👍 👍 👍

@yaru22

This comment has been minimized.

Show comment
Hide comment
@yaru22

yaru22 commented Oct 12, 2014

+1

@manland

This comment has been minimized.

Show comment
Hide comment
@manland

manland commented Oct 12, 2014

+1

@lgalfaso

This comment has been minimized.

Show comment
Hide comment
@lgalfaso

lgalfaso Jan 7, 2015

Member

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

Member

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

This comment has been minimized.

Show comment
Hide comment
@metamatt

metamatt Jun 11, 2015

Contributor

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.

Contributor

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

This comment has been minimized.

Show comment
Hide comment
@benjamingr

benjamingr Jun 11, 2015

Contributor

@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).

Contributor

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

This comment has been minimized.

Show comment
Hide comment
@metamatt

metamatt Jun 11, 2015

Contributor

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

Contributor

metamatt commented Jun 11, 2015

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

@metamatt

This comment has been minimized.

Show comment
Hide comment
@metamatt

metamatt Jun 11, 2015

Contributor

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.

Contributor

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

This comment has been minimized.

Show comment
Hide comment
@sariyu

sariyu commented Oct 1, 2015

+1

@lifenautjoe

This comment has been minimized.

Show comment
Hide comment
@lifenautjoe

lifenautjoe commented Oct 13, 2015

+1

@yoeen

This comment has been minimized.

Show comment
Hide comment
@yoeen

yoeen commented Dec 18, 2015

+1

@lgalfaso

This comment has been minimized.

Show comment
Hide comment
@lgalfaso

lgalfaso Dec 18, 2015

Member

#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

Member

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.