nativescript-angular
nativescript-angular copied to clipboard
Multiple async/await resulting in "Navigation triggered outside Angular zone" in NS7 Angular 10.
Environment √ Component nativescript has 7.0.11 version and is up to date. √ Component @nativescript/core has 7.0.13 version and is up to date. √ Component @nativescript/android has 7.0.1 version and is up to date. @angular/core 10.1.6 @nativescript/angular 10.0.0
Describe the bug
I am upgrading a project to NS7/Angular 10.
I have a button bound to an async method.
In the method if you await the result of something, then await a call to RouterExtensions.navigat(),
Then the error:
Navigation triggered outside Angular zone, did you forget to call 'ngZone.run()'
will be printed to the console.
There was no error in NS6 / Angular 8 in this scenario.
To Reproduce
<Button text="Log In" (tap)="onLogin()"></Button>
onLogin = async (): Promise<void> => {
console.log("op1");
await this.delay(1000);
console.log("op2");
await this.routerExtensions.navigate(["login"], { clearHistory: false });
}
async delay(ms: number) {
return new Promise(resolve => setTimeout(resolve, ms));
}
Is your tsconfig set to produce ES2017? I noticed zone issues popping up with async/await after the latest nativescript releases.
See https://github.com/angular/angular/issues/31730, zone doesn't support native async/await in ES2017 targets.
Workaround is to wrap your NgZone stuff in a ngzone.run(() => { [... something to do in a zone]})
call or to fall back to Promises where this doesn't work.
Yes set to ES2017 alight. that is a real gotcha with moving to NS10.
I believe Angular in general is making a move away from zone.js
. With the new async/await, it becomes impossible to target es2017 and have angular work consistently. With IE finally dying, things like async/await are already supported for 94.42% of users (https://caniuse.com/?search=async), and it seems like es5 might be on the way out in the coming years. This means that libraries will likely drop es5 support, and targeting es2017 or lower will not save you from zone not working, so the angular team will have to come up with something akin to ngcc
to transform native async/await to es5 or add babel
to the webpack config.
Zone also has a number of issues currently. If you run:
this.zone.runOutsideAngular(() => {
setTimeout(() => {
for(let i=0; i<100; i++) {
this.zone.run(() => {});
}
}, 0);
});
Change detection will run 100 times (basically every time you exit the angular zone through zone.run
). Thankfully there are a couple of things that improve it, ngZoneEventCoalescing
will coalesce events into a single CD and ngZoneRunCoalescing
(set to be released in 11.1, implemented in 11.1.0-next.0).
Coalescing is also the way angular ivy's new markDirty
API works, which I believe will be the new standard for CD. markDirty
marks the component+all parents up to the root as dirty and schedules a CD with requestAnimationFrame
or setTimeout
if not already scheduled.
This is 100% my speculation, but I expect zone to be deprecated in the future (not very soon though) as people are encouraged to use markDirty(this)
or ChangeDetectorRef
, or even better, async
and other pipes (which will probably be adapted to schedule CD).
Zone absolutely was a great idea at the time and it's amazing how well it worked so far, but as javascript evolves, it'll suffer more and more due to not being considered by TC39 and the proposal itself being withdrawn (https://github.com/domenic/zones). Zone itself works by monkey patching APIs, which is also why a lot of things in native platforms like NativeScript require you to manually call ngzone.run
(with our rewrite of the angular integration, this will be greatly improved. Also we'll recommend turning on ngZoneEventCoalescing
and ngZoneRunCoalescing
for very big performance improvements!)
Here are some great resources:
Reactive Angular with ngrx/component: https://christiankohler.net/reactive-angular-with-ngrx-component "Zones are great until they are not.", Rob Wormald - Keynote - What’s New and Coming in Angular | AngularUP 2019: https://www.youtube.com/watch?v=-32nh-pGXaU&feature=youtu.be&t=19m10s
try to produce ES2015
"target": "es2015"
does fix the problem for me, @iamabs2001 .
I'm doing some more testing to make sure it doesn't break anything before downgrading