angular with tsconfig target ES2017 async/await will not work with zone.js #31730
Comments
|
/FYI @domenic The short answer is that it is not possible to intercept native promises in the VM. (Native promises is used by We have two approaches to this problem:
Out of the two approaches I think second one ( This effort has been a bit on back-burner, but I will restart the discussions with @domenic. |
|
@mhevery , got it, I will also do some research about the v8 hooks. thank you! |
|
node.js begin to support https://github.com/nodejs/node-eps/blob/master/006-asynchooks-api.md so I will try to implement zone.js with @mhevery , do you think this is the correct direction? please review. |
|
Yes that is. I have worked with the people on async hook, and it should
work well with Zone. That means that we should be able to drop the Promise
polyfiil for node.
…On Sun, May 21, 2017 at 9:37 AM, JiaLiPassion ***@***.***> wrote:
node.js begin to support async_hooks,
https://github.com/nodejs/node-eps/blob/master/006-asynchooks-api.md
nodejs/node#13000 <nodejs/node#13000>
nodejs/node#13139 (comment)
<nodejs/node#13139 (comment)>
so I will try to implement zone.js with async_hooks in node.js, it should
have better performance, and can handle native async/await issue in
node.js.
@mhevery <https://github.com/mhevery> , do you think this is the correct
direction? please review.
—
You are receiving this because you were mentioned.
Reply to this email directly, view it on GitHub
<https://github.com/angular/zone.js/issues/740#issuecomment-302947655>,
or mute the thread
<https://github.com/notifications/unsubscribe-auth/AAG1T1cGv4snMB_vUhuz71k5GM8jMwWvks5r8Gg4gaJpZM4M-wuM>
.
|
|
@mhevery , got it, I will try to use |
|
@JiaLiPassion - Awesome stuff! |
|
@amcdnl, this is still under development, it will take some time, and I have to wait for nodejs to provide some additional information to finish this one. |
|
Thanks for the update @JiaLiPassion ... I was able to temp work around it by transpiling my code from ES2017 to ES2016 which sucks but is ok for short term. |
|
Has there been any progress on this one? As far as my limited skill of perception can see, this is the main issue that prevents angulars change detection work properly with es2017's async/await. ES2017 is realy helpfull for debugging as the compiled sourcecode by typescript/angular is not that different to the real source and so sourcemaps work much much better. Thanks for your time, Tim. |
|
@JiaLiPassion can you say something about the current status? |
|
@enko, @shellmann, this issue is still pending because there is no proper way to patch So now, sorry this issue can not be resolved, so Angular can not work with |
|
@JiaLiPassion Would that be https://github.com/domenic/zones? |
|
@enko, yes, it is, it has been in stage 0 for about two years... |
|
@JiaLiPassion How about node.js now? Can we resolve the question with async_hook? has examples? thx! |
|
@royalrover, yes, with |
|
Just want to point out the "obvious" workaround for Node.js, which is to downgrade target to ES2015 |
|
I get it that with current state of browsers this is not possible to fix by Angular team. However, it's a huge issue going forward with Angular lacking support for not-really-that-recent ES standards. As I get it, Google uses Angular a lot internally, there is some MS involvement as well and I get also that this issue requires "political" actions, not technical ones. I hope people on Angular team understand how severe problems this little tiny issue #740 is going to cause moving forward, if those "political issues" to push browser support are not taken seriously by some more powerful entities within Google and maybe within MS as well. If those two giants were serious about the issue, it would be already fixed by now. For now, the obvious workaround for the issue is just sitting on ES2015 target forever with horrendous generated async/await code, lack of optimizations and lack of proper debugging due to that generated code. If we are just sitting and waiting "Zones for JavaScript" proposal to get anywhere from stage 0 of the TC39 process, after that was presented at the January 2016 TC39 meeting, we can be just waiting forever, without any hope about Angular ever supporting web standards going forward. You don't possess the power, but your bosses, or bosses of your bosses, or bosses of bosses of your bosses do possess the power, so please, push them hard! |
|
I've followed this issue for a while now of curiosity and I think it's time to share my own solution to the problem, implemented in Dexie version 2.0. It has been used for quite a while and works across all browsers keeping the zones between await calls. My first beta was released in October 2016, and the first stable 2.0 release in September 2017. Dexie has ~15k weekly downloads on npm and there are no show-stopping issues related to its zone system. The reason for having a zone system in Dexie is to keep track of ongoing transactions. First version with the zone system built-in was release in 2014 (but then without support for async/await). This was before I knew about the concept of zones, so I thought it was my own invention at first, and called it PSD (Promise-Specific Data) as it only cares about promise-based async flows. The 2.0 version use something I call zone-echoing. It will enque micro-tasks that will re-enter the zone in coming micro-tasks. It can know when it's time to stop the zone echoing as long as the user sticks to its own Promises. A fallback will stop it by a limit. This works across all modern browsers and does not leak zones (except for MutationObserver subscriptions, whose events will derive the zone from the zone where the subscription was initialized - which wouldn't be a problem in angular/zone.js as it also patches those events). The technique I use could be used in angular/zone.js but then every promise-returning function in the DOM would have to return a zone's own Promise (maybe it already does?) in order to invoke the zone echoing. There could be performance implications that would need to be considered. Especially as I detect native await calls by defining Promise.prototype.then using a getter instead of a value, and have to echo zones in order to support calls that awaits non-promises. If interested, the implementation lies in Dexie repo at src/helpers/promise.js. |
|
@dfahlander, thank you for the post, I will definitely read it, and I will contact you if I have any questions. |
|
@dfahlander, thanks for your post again, I have sent you an email, please check it. |
|
@dfahlander, I think I have a basic understanding of your code, I tried the same way, it works in some cases, but not work in the case below.
In this case, I think Not sure my understanding is correct, please confirm, thanks! |
|
That is true. Maybe the check |
|
@dfahlander, thanks for the response, in this case,
the return value of function |
|
True. Unless accompanied with a coding guideline of how to utilize zones together with native async await. |
|
@dfahlander, thanks for the clarification! |
|
Should this ticket have someone assigned on it? Now ES2019 (and ES2018) have been finalized, and Angular 8 is coming with support for producing both legacy (ES5) and modern (ES2015+) Javascript bundles. However, there is not much point of that, if modern ES versions ES2017, ES2018 and ES2019 are not supported anyway in Angular. Is there any way to overcome this problem either by fixing it somehow or at least being able to use ES2017 or ES2018 target, but somehow use non-native async/await for time being? |
|
Excuse my possibly stupid question, but why can't zone.js override the global |
|
@rvalimaki, this is a very difficult issue to resolve, I am still trying to find out a walk around. |
|
@JiaLiPassion thanks for clarifying |
|
The |
|
Suppose one is willing to manually run |
|
The ice has broken angular/angular-cli@e2e8d57 |
|
That's great news, but I do hope the team will keep exploring ways to enable this outside of angular. I use zonejs outside of angular for many purposes |
I strongly agree. One option to consider is using ahead of time compilation to decorate all async operations with needed state preservation idioms. This approach would assume of course that you aren't doing any runtime loading of uninstrumented code. |
|
Yeah maybe, personally I'm thinking the best bang for our buck is petitioning the Typescript developers to allow for downleveled async/await on higher ES targets. Might be an uphill battle since Zone.js users are probably one of the only groups who really need it :-\ |
|
@rezonant best bang for buck is probably moving Angular users away from Zone. It's not really pulling its weight anymore, and keeps us stuck in the past |
|
The zone concept still has significant potential. The problem is that it has been expected its native implementation in browsers spec. |
|
You are always free to use a different change detection strategy other than Zone.js. Personally for Angular I use (and prefer) Zone.js by default and use Zone escape (via It's fine if you as a developer want to do that, but it's never as easy as "just remove Zone.js" :-) More importantly, and separately from the debate about state management, Zone.js solves use cases that are impossible, leaky or difficult without it, which have little or nothing to do with Angular:
These are just the use cases I myself have used it for over the last few years. There are countless more that have yet to be thought of. Zone.js is a fantastic library, and I can only hope that it can make the leap to supporting async/await so we can enjoy it until the ES committee can be persuaded to adopt support for something like it into the language and the runtime. All this is not to say that the library itself does not have rough edges, or that there aren't paths for improvements.
|
|
@rezonant those are some noble ideas, but Zone.js is all but dead. It doesn't even work with Node.js async/await, despite async_hooks being available. The Zone.js API is overwrought and over complicated. Its carcass was merged into the Angular repo from its original home because no one else had any independent interest in it. |
@pauldraper any chance you can point to a specific bug or failing test case? |
|
You mean that Node.js native async/await is not supported? |
|
Yes, thank you. I am using zone.js because it's a necessary dependency of opentelemetry in the browser. Unfortunate that such a new observability project is so dependent on a fragile and aging approach. |
I agree, that's why I started modelling an alternative API which solves the same problem-space in a simpler way. Check out https://github.com/rezonant/rezone . The API is much simpler, and might be a more approachable path to standardization. The prototype implementation is built on top of Zone.js for simplicity. However, the design of Zone.js' API does not invalidate the underlying concepts; we still need a way to observe the lifecycle of chains of asynchronous operations at runtime.
@pauldraper I think this is a bit harsh. You might consider the developers who originally built it and all the challenges they had to solve, and how much value the entire community has extracted from it, even though it seems to be a punching bag amongst the under-informed in the community. Myself and others are posting here because we not only see the potential of what Zones in Javascript can do, but that we are already making use of it in production scenarios today (outside of Angular). Honestly it's not surprising that it took years for anyone to realize how to apply Zone.js to other use cases. The concepts themselves are subtle, and as we've both pointed out, the library itself (and certainly its documentation) is rough, and the API design is, yes, overcomplicated and formidable. One need only search for "Zone Angular" to see the reams of "What exactly is Zone.js" and "How exactly does Zone.js work" articles which themselves are a bit hand wavey and overly focused on the use case of change detection.
Though I'm not familar with OpenTelemetry's codebase, I'm familiar with what it is meant to do, and I have no doubt it is using Zone.js specifically for the type of benefits I mention above- it is able to track execution contexts across asynchronous calls. As far as I'm aware, there is no other way to do that other than hotpatching the APIs like Zone.js does. It's not that the approach is aging, but simply that the final design of ES' async/await did not allow for interception as needed for a user-land observability framework like Zone.js. Trying to veer back on to the task at hand, let me sum up the possibilities for how to solve native async/await in Zone.js generally:
For anyone new to the issue: Support for native async/await can't be trivially implemented because Node.js' async_hooks (not supported in the browser, nor Deno, nor other ES runtimes) is part of the solution, but the implementation work has been lagging behind for quite some time. EDIT: Edited to add note that TS downleveling can't solve for libraries |
|
Looking to TS for a solution is flawed. Besides being unworkable for libraries, it also breaks the ability to use Angular from JS. Otherwise, I agree. IIRC there was an early proposal for async_hooks-like standard for ES or browsers, but I can't remember what it was. My criticisms is of Zone.js, not context tracing in general. (Complain all you want about there not being standard runtime support available, but Zone.js doesn't even use mechanisms for Node.js that were available...it was effectively abandoned long ago, perhaps because of its flawed design, perhaps other reasons.) I look with great interest towards the success of your project, @rezonant . |
|
I will try to support |
|
Ah of course- that does explain the difficulty-- wrapping the task handler to detect exceptions would be needed for Razmin and probably OpenTelemetry (assuming it has exception tracing), but for Angular it might be enough. I wonder though: is async_hooks enough to allow introspecting the zone heirarchy? If not, that means zone-locals would be out as well. If the current zone can be ascertained then perhaps async_hooks + smart global exception handling would do the trick to support onError- and ironically Zone.js API can handle this better than rezone API, which fundamentally needs the ability to modify task callbacks. At least with Zone's setup the way the onError hook is executed is abstracted out more. I'm curious if there are other known use cases for modifying task callbacks (other than catching exceptions)? EDIT: After a bit more thought, it must be possible to establish the Zone heirarchy, assuming before()/after() happen within the same turn as the task handler (documentation doesn't seem to be clear on that) |
|
Wait, isn't this a bit of a problem?
|
|
zonejs was a mistake. |
|
@avatsaev: Though it's arguably not the best place to debate the merits of Zone.js, given that we have a lot of work before this issue can be fully closed, I'd welcome your thoughts on why you feel that way, provided Jia Li or any other Angular team members / moderators don't object, as after all, this is their venue. |
There is a lot of why, here is just a short list:
Not saying that Angular itself is a bad thing and of course not saying that |
|
I'm going to approach your post as an Angular (frontend) developer. As I've noted above, Zone.js is far more useful outside of Angular than in, but it's clear you are coming at it from that angle, and there's plenty to discuss. Before I get into a detailed response, I'll note that I've heard all this before, many times. What I haven't heard is folks with a deep understanding of Zone.js and the real down-to-the-metal performance impacts of Angular as a framework weigh in too often. I hope my two cents is useful as someone who has built heavily performance intensive apps; I have built several realtime audio/video processing apps for the broadcast industry built with Angular. I'm not saying that any of what you've said is necessarily incorrect, just that it feels misguided to me, and doesn't fit my experience of the technology stack based on the projects I've worked on.
This word is doing a lot here. Let me address it this way: As an Angular app developer, when it comes to Zone.js and how its used in Angular, I only care about the following things:
For change detection, you are correct. But this isn't going to be relevant when you are doing the typical Angular app- it will be relevant when you are doing requestAnimationFrame(), tight timers, or other code which frequently waits for promises to complete. When this happens, do the above: Escape from the zone, and return to do change detection. Don't rearchitect your entire application simply to serve your 1% use case- most of your app is forms over data, and Zone.js' change detection strategy works for that. The escape is there for the 1% where it doesn't.
Maintained as new async web APIs are added yes. Not maintained by the app developer of course, but maintained by the Zone.js developers themselves. So then the question becomes, how often are new async opportunities added to Web APIs? Well, you can look at the commits in https://github.com/angular/angular/commits/master/packages/zone.js where Jia Li has been maintaining Zone.js, and really there hasn't been much needed to keep it working (with the async/await-pocalypse the main exception). Seems like more work has gone into Bazel and testing which was going to be done regardless of the merits of Zone.js as a library.
Monkey patching is adding new user-land functionality to existing interfaces. Zone.js doesn't do this, it instruments those interfaces. I would agree if Zone added new methods to String, or modified the parameters that Regexp took. But this isn't the case.
This is possible, and I think we'd all like to see Zones as a language primitive where it has no chance of this happening, but the runtime timing model of Javascript is such that there ultimately are only 3 critical concepts: The macrotask, the microtask, and the turn. This is how every Javascript runtime you've ever used is constructed, and Zone.js only needs to layer on instrumentation for recording when these concepts are scheduled and when they complete. Thus, Zone.js need only express the scheduling and macro/micro task behavior of the API it is patching. It's complex when you look under the hood, but the runtime semantics of setTimeout don't change with every newly published ES release, so the chance of breaking semantics is rather low for a given API once the patching is stable. Would love to hear about concrete cases where you've seen Zone.js modify runtime semantics (other than stack traces
Yes, but as I've noted in previous posts and have hinted at above, it is easy to equate the cost of Angular's change detection cycle with Zone.js itself. Remember, all Zone.js does is let Angular know when your callback is done executing, it is the actual change detection process which is expensive. import 'zone.js';
import { platformBrowserDynamic } from '@angular/platform-browser-dynamic';
import { AppModule } from './app/app.module';
import { environment } from './environments/environment';
let zone = Zone.current.fork();
zone.run(() => {
platformBrowserDynamic().bootstrapModule(AppModule)
.catch(err => console.error(err));
})Technically speaking, running Angular in a Zone isn't even required for Zone to be active. Just loading it will cause all the patches to be in place. Nonetheless, I'm quite certain that almost all of the perceived performance issues you are referring to will be gone, because the expensive part is change detection, not Zone.js itself. And this is why disabling change detection with NgZone#runOutsideAngular is so useful for managing performance overhead, not because it eliminates the overhead of Zone.js, but because it eliminates the overhead of extra change detection cycles.
I'll grant you that it is possible a future feature could cause a problem, but that's not a reason to ignore the use cases that only Zone.js or a similar context tracing / observability solution enables.
This is only a major concern if you are thinking that you will need to remove Zone.js for either (A) performance reasons (see above) or (B) compatibility problems causing zone escape, of which (other than bugs found and fixed in Zone.js along the way) there has really only been one existential problem: async/await. In the case of bugs with Zone.js that have existed before which may have allowed your callbacks to modify Angular's model state without a corresponding change detection cycle, you always had the option of re-entering the zone in that specific circumstance to rectify the issue. So far it has only been async/await which has not had an easy straightforward fix.
It's the default because most apps are forms over data. The "just work" part of it has always been Angular's promise, and Zone.js' use in Angular 2+ was just the next obvious step. If you remember back in the Angular.js days, you might remember that the built-in HTTP service would trigger
This is what a framework is for. Angular also hides how efficient virtual DOM diffing works, and how the dependency injection container is properly constructed from your disparate sets of DI providers. That doesn't make it an inherently bad thing.
Do your thing, but I personally am not a fan of using reactive stores or ngrx. Service with a Subject is more than enough when used properly, and is a more composable model than dictating a central data store for the entire application. The complexity introduced by reactive store models is far from free and time travel debugging isn't enough for me to overhaul the entire application architecture. |
|
@rezonant, the information you provided I think might help someone to fight the temper of zone.js. I was not really saying that zone.js is not manageable but that it's, in my opinion, not a forward-looking idea and that it's hard to get rid of it. I just tend to agree with the above message that it was a mistake to introduce the zone.js thing but of course it depends on a lot of things (team, budget, kind of project, etc). It would be fine though, in my opinion, to use it for prototype-like projects (so should be disabled by default).
Not necessarily central, it's not dictated but flexible. It might be mixed or just component scoped store, sort of
Ngrx uses subjects internally, behavior-like one I think. The point here is unification, so you jump between projects in a productive manner since there is no need to first dive into the fancy custom built on subjects store management library. Ngrx, btw, was named as an example.
Thank you. But that was a way to go if I'm forced to start something new on Angular which is not the case. |
|
So just my noobish cents here: will there be a fix for that in zone.js? I am using Ionic with Capacitor and the Capacitor team is suggesting ES2017 for plugins, so it might be beneficial to update the app to that too. But the message in the console is annoying and having no change detection is also not a good way ^^. I did never get behind the zone.js thing because looking at other Frameworks change detection works pretty neat without it (Svelte, Vue, preact, etc.), so I would like to hear more about this "removing zone.js and replacing it with X". Any tutorials or deeper information/docs I can read about the pro/cons and alternatives? I am looking forward for a simpler more integrated CD in Angular :). |
|
I believe Angular 11.2 will have support for ES2017+ when it's released if all goes well, so you can start targeting that ES level with that version and still use zonejs. If you want to opt out of automatic change detection, then you need to handle it via some other means or do it manually. Look up ChangeDetectionStrategy. The options are to use a reactive store approach like ngrx or to manually trigger change detection when you need the view to update. If you already have a sizable app using zonejs, as myself and others have pointed out, it may be expensive to retrofit a reactive store into your app. If you are already on Angular 11 then it's probably best to adopt 11.2 when it comes out. |
ES2017+ TS target but excluding native async/await since this part will be internally transpiled (TS doesn't support partial switches but just "target" so custom transpilling needed)? |
|
Yes it down levels async/await using babel internally in the angular compiler, so it should be transparent to the app developer. If you are not using angular CLI then you wouldn't benefit from the chanhe, but then again you always could have put the babel plugin into your custom webpack configuration, so that could be set up in a project today if desired. The only downsides on that is perhaps less efficiency (since native async/await can benefit from runtime optimizations) but that was already the case on ES2016 target and at least you'll be able to use newer ES features in your codebase, and libraries using native async/await will be automatically downlevelled as well, so no worries about using libraries that make use of that feature, whereas before it was a subtle foot gun, since you couldn't control whether a lib used native async/await without a compiler pass. It's a nice stopgap to ensure devs can continue to target newer ES versions, and this issue is here to track progress around a more wholistic solution that doesn't need to downlevel async/await while still ensuring code cannot unintentionally escape from a zone. |
I think it should be the default, but I wouldn't be too upset if it were made not to be -- I'd hope Angular continues to offer it as an option regardless, but even if they don't, it is actually quite trivial to implement Zone based change detection without Angular's help so I would probably add it back in and publish it as an addon package for those who want to use it :-)
So to be clear, I'm not talking about building a store based on Subject. I'm talking about building services which expose Observables. "Service with a Subject" is just the popular vernacular for the approach, which stands in contrast to the recent uptick in interest in React style data management strategies like Redux, Flux, Ngrx, or other reactive stores. "Service with a Subject" isn't really a replacement for a reactive store itself, it is just a way of organizing your app's data flow, which almost any Angular developer is probably already familiar with, even if they didn't know that there was a term for it. The canonical example is a UserService that tracks the current user: @Injectable()
export class UserService {
private _userChanged = new BehaviorSubject<User>(null);
get userChanged() {
return this._userChanged;
}
// ....
}A component interested in the current user would subscribe: @Component()
export class LoginStatusComponent implements OnInit, OnDestroy {
constructor(
private userService : UserService
) {
}
private subsink = new SubSink();
user : User;
ngOnInit() {
this.subsink.add(this.userService.userChanged.subscribe(user => this.user = user));
}
ngOnDestroy() {
this.subsink.unsubscribe();
}
}I imagine there are a great many of these UserService (or AuthService, AccountService, LoginService, whatever you decide to call it) within the Angular apps out in production right now, using this sort of model. But you can use this for everything- even for "instance" style subscriptions. Say you have a This really doesn't have anything to do with change detection, but simply data flow in your application. And indeed, reactive store solutions also don't have anything to do with change detection, except that you can hook into when the state has been definitively updated, and only trigger change detection when that happens.
I think you mean "conformity" :-)
I guess the difference is I proudly start new apps in Angular to this day. If you would prefer starting an app in a different framework that's great, there's nothing wrong with that, but I prefer Angular over the alternatives (though I do enjoy learning about those frameworks and the new concepts arising from them, and I certainly don't think Angular is the perfect framework). Nonetheless, thank you @vladimiry for the lively and civil debate about the pros/cons on this topic, and thanks for all subscribers to this issue for entertaining the discussion :-) |

Formed in 2009, the Archive Team (not to be confused with the archive.org Archive-It Team) is a rogue archivist collective dedicated to saving copies of rapidly dying or deleted websites for the sake of history and digital heritage. The group is 100% composed of volunteers and interested parties, and has expanded into a large amount of related projects for saving online and digital history.

this issue is similar with #715, if we use chrome v8 async/await and compile angular with tsconfig target 'ES2017', then typescript will not generate __awaiter code and use native async/await.
and the following logic will fail
unlike typescript transpiler, native async/await will first yield from test, and then call promise.then
for continuation when await something. So Zone currentFrame will become root.
The sequence of above logic when call
await delay(100)will look likePromise.prototype.thenBased on the spec,
https://tc39.github.io/ecmascript-asyncawait/#abstract-ops-async-function-await
Step8 is not be executed immediately but in the microTask queue after the current function execution.
I checked Chrome v8 source,
https://chromium.googlesource.com/v8/v8/+/refs/heads/5.5.10/src/js/promise.js
ZoneAwarePromise is not treated as native one, so Chrome v8 enqueue a micro task to perform
thencall. This maybe the reason.And it seems the logic is totally changed in v8 6.0, so I will try the chromium 6.0 to see what happened.
The text was updated successfully, but these errors were encountered: