Angular.js: ES7 async await transplied using babel

Created on 19 Mar 2015  ยท  11Comments  ยท  Source: angular/angular.js

Hi, when I use ES7 async/await transpiled using babel like this

await $http.get(...)
$scope.doSomething()

$digest loop is not called for some reason. I understand angular 1.* branch might not target some of the newer features but it would be wonderful if, at least, transpiled code worked as intented.

Most helpful comment

To address a few points from above:

Why won't it? Breaking changes are still being made. We could have a v1.7

Because we _can_ make breaking change, it doesn't mean we _should_. We need to weight the benefit versus the impact. A change like this would have huge implications in how AngularJS applications work, how they are tested etc. It will break too many people to be worth the risk imo.

Keep in mind that AngularJS is a mature framework and we should put more emphasis on stability rather than "moving fast and breaking things". At this point, it is much easier and safer to monkey-patch Promise
with $q or some other library, as you said.

#

As @frederikprijck mentioned, AngularJS takes a quite different approach to change detection than Angular. Basically, both need a way to detect when certain events of interest have happened, that may have affected the application state (e.g. a DOM event, an XHR requests, a promise resolution, etc).

  • AngularJS creates wrappers for these APIs and you need to use these wrappers, which (in addition to calling the underlying, native API) will notify AngularJS that "something happened" and it needs to run the change detection.

  • Angular, on the other hand, monkey-patches the native APIs (via zones), which alleviates the need to use wrappers. Under the hood, the monkey-patched versions essentially do what AngularJS' wrappers do: They call the original, native APIs and notify Angular when the operation is completed.

(In both frameworks, there are ways to "escape" the change detection or opt-in "from the outside", but that is not relevant here.)

#

In my eyes, the main conceptual difference between the two approaches is that one is opt-in (AngularJS) and the other is opt-out (Angular). What I mean by that:

  • opt-in: In AngularJS, you need to opt into this change detection by using the special wrapper services. "By default", i.e. if you use native, non-AngularJS APIs you are outside of this change-detection-aware sphere.
  • opt-out: In Angular, you are by default into the change detection zone. You need to explicitly opt-out if you need/want to.

Yes, this is an oversimplification, but ยฏ\_(ใƒ„)_/ยฏ

Keep in mind that this generally affects not only _your_ code, but the 3rd-party code you use in your application. In AngularJS, if you wanted to use a generic (non-AngularJS) 3rd-party library, you needed to create a wrapper/adapter to wire it to AngularJS' change detection (via the wrapper services). In Angular, this pain is shifted over to zones, which can ensure that you these events are captured without extra work on your part.

It seems that the Angular approach leaves you with less things to worry about, but it comes with its own challenges. For example, you may get a lot more "triggers" than you expect or want. Angular's injection system and component-tree-based architecture were specifically designed to overcome these chanllenges and thus give users an easier API without the extra cost.

#

Another area in which this has considerable consequences is testing. AngularJS relies on the fact that developers are going to use its wrappers and it can easily instrument them for testing purposes. If this ceases to be true, the whole framework will be affected and the way you write your tests will need to change drastically. (Who wants to rewrite all their tests in order to upgrade from v1.6.x to v1.7.x? You can use async/await out-of-the-box ๐Ÿ˜›)

#

So, in short, while I am not saying it is not possible, it is a much bigger undertaking and cannot be treated as an isolated feature ("Hey, let's switch from $q to window.Promise"). It necessarily needs to be part of a greater internal re-design that will have a big impact on existing applications.

During this re-design, we would unavoidably introduce a ton of bugs and break a few dozens obscure cornercases :smiley: (And we would eventually end up with a version of Angular 2+, plus some extra technical dept ๐Ÿ˜›)

Taking all that into account, I believe it is to the best interest of AngularJS users to refrain from such ground breaking changes and instead focus on stability and adding features that AngularJS is more well-equipped to adapt to.

And while we _have_ discussed integrating zones with AngularJS in the past (and haven't ruled it out entirely), I don't see it happening any time soon ๐Ÿ˜›

#

Take all this with a grain of salt. It is just my point of view and I have been (very occasionally ๐Ÿ˜›) wrong before.

All 11 comments

Wouldn't $http do $rootScope.$digest(), then await takes over? If that is the case, it shouldn't be surprising that a $digest does not occur.

Without looking digging into Babel code, it would be very hard to know this for sure, so please take my comment with a grain of salt.
I would expect Babel to transform

await $http.get(...)
$scope.doSomething()

to something equivalent to

Promise.all([$http.get(...)]).then(() => {
  $scope.doSomething();
});

[Note: this should be a lot more complex, as it needs to handle generators, when _await_ is in the middle of the expression and _return_ statements; This is this might get transformed to a mix of _yield_, Promise and generators transformations]

Again, I am not sure, but this is what I would expect.

If this is true, here is when things get complicated. Angular uses Promise/A+ and Babel uses ES6 Promises. On top, Angular $q are resolved within a digest and ES6 Promises are resolved with their own mechanism. Anyhow, this should be enough to understand why these two approaches are incompatible.

The only way to make this work would be for Babel to generate code that is equivalent to

$http.get(...).then(() => {
  $scope.doSomething();
});

I do not know how easy that would be, nor if this is in their best interest, but I would ping them to figure this out.

With these constraints, I think there is nothing in Angular to do as Angular 1 is not going to change from Promise/A+ to ES6 Promise (Angular 2 already did this change).

@lgalfaso is spot on with why it doesn't work. I'm pretty sure that according to the specs this shouldn't even work if async/await is supported natively. It is however possible to create async/await like behaviour that does work properly with angular by using generator functions. I just did so, you can find it here: https://github.com/Magnetme/ng-async. It's slightly less pretty then the real deal, but it does work properly with Angular 1.x.

@sochka you can probably use zone.js https://github.com/oricalvo/angular1-zones

With these constraints, I think there is nothing in Angular to do as Angular 1 is not going to change from Promise/A+ to ES6 Promise (Angular 2 already did this change).

Why won't it? Breaking changes are still being made. We could have a v1.7

@stevenvachon Changing from Promise/A+ to ES6 Promise would probably require changes to the way AngularJS does its change detection (wrapping it like $q does might be a bit harder). The need to change this (to have less need for wrapping things the way AngularJS does) is one of the things which lead to Angular imho.

You can always have a look at the zonejs implementation https://github.com/angular/angular.js/issues/11370#issuecomment-252110474 (haven't used it tho)

Changing from Promise/A+ to ES6 Promise would probably require changes to the way AngularJS does its change detection

$q could remain, only wrap ES2015 Promise (with a polyfill). AngularJS has a large test suite that could likely catch most of the issues during its development.

You can always have a look at the zonejs implementation

That seems like a complicated patch. Is it much better than patching window.Promise with $q or babel-plugin-promise-to-q?

@stevenvachon zone is heading through TC39

@graingert is zone the same as Realm?

No realms the different globals in workers tabs and iframes

To address a few points from above:

Why won't it? Breaking changes are still being made. We could have a v1.7

Because we _can_ make breaking change, it doesn't mean we _should_. We need to weight the benefit versus the impact. A change like this would have huge implications in how AngularJS applications work, how they are tested etc. It will break too many people to be worth the risk imo.

Keep in mind that AngularJS is a mature framework and we should put more emphasis on stability rather than "moving fast and breaking things". At this point, it is much easier and safer to monkey-patch Promise
with $q or some other library, as you said.

#

As @frederikprijck mentioned, AngularJS takes a quite different approach to change detection than Angular. Basically, both need a way to detect when certain events of interest have happened, that may have affected the application state (e.g. a DOM event, an XHR requests, a promise resolution, etc).

  • AngularJS creates wrappers for these APIs and you need to use these wrappers, which (in addition to calling the underlying, native API) will notify AngularJS that "something happened" and it needs to run the change detection.

  • Angular, on the other hand, monkey-patches the native APIs (via zones), which alleviates the need to use wrappers. Under the hood, the monkey-patched versions essentially do what AngularJS' wrappers do: They call the original, native APIs and notify Angular when the operation is completed.

(In both frameworks, there are ways to "escape" the change detection or opt-in "from the outside", but that is not relevant here.)

#

In my eyes, the main conceptual difference between the two approaches is that one is opt-in (AngularJS) and the other is opt-out (Angular). What I mean by that:

  • opt-in: In AngularJS, you need to opt into this change detection by using the special wrapper services. "By default", i.e. if you use native, non-AngularJS APIs you are outside of this change-detection-aware sphere.
  • opt-out: In Angular, you are by default into the change detection zone. You need to explicitly opt-out if you need/want to.

Yes, this is an oversimplification, but ยฏ\_(ใƒ„)_/ยฏ

Keep in mind that this generally affects not only _your_ code, but the 3rd-party code you use in your application. In AngularJS, if you wanted to use a generic (non-AngularJS) 3rd-party library, you needed to create a wrapper/adapter to wire it to AngularJS' change detection (via the wrapper services). In Angular, this pain is shifted over to zones, which can ensure that you these events are captured without extra work on your part.

It seems that the Angular approach leaves you with less things to worry about, but it comes with its own challenges. For example, you may get a lot more "triggers" than you expect or want. Angular's injection system and component-tree-based architecture were specifically designed to overcome these chanllenges and thus give users an easier API without the extra cost.

#

Another area in which this has considerable consequences is testing. AngularJS relies on the fact that developers are going to use its wrappers and it can easily instrument them for testing purposes. If this ceases to be true, the whole framework will be affected and the way you write your tests will need to change drastically. (Who wants to rewrite all their tests in order to upgrade from v1.6.x to v1.7.x? You can use async/await out-of-the-box ๐Ÿ˜›)

#

So, in short, while I am not saying it is not possible, it is a much bigger undertaking and cannot be treated as an isolated feature ("Hey, let's switch from $q to window.Promise"). It necessarily needs to be part of a greater internal re-design that will have a big impact on existing applications.

During this re-design, we would unavoidably introduce a ton of bugs and break a few dozens obscure cornercases :smiley: (And we would eventually end up with a version of Angular 2+, plus some extra technical dept ๐Ÿ˜›)

Taking all that into account, I believe it is to the best interest of AngularJS users to refrain from such ground breaking changes and instead focus on stability and adding features that AngularJS is more well-equipped to adapt to.

And while we _have_ discussed integrating zones with AngularJS in the past (and haven't ruled it out entirely), I don't see it happening any time soon ๐Ÿ˜›

#

Take all this with a grain of salt. It is just my point of view and I have been (very occasionally ๐Ÿ˜›) wrong before.

Was this page helpful?
0 / 5 - 0 ratings