Now, obviously, Angular will not “know”, that ngOnInit has become async. I feel that this is not a problem: My app still works as before.
Semantically it will compile fine and run as expected, but the convenience of writing async / wait
comes at a cost of error handling, and I think it should be avoid.
Let's look at what happens.
What happens when a promise is rejected:
public ngOnInit() {
const p = new Promise((resolver, reject) => reject(-1));
}
The above generates the following stack trace:
core.js:6014 ERROR Error: Uncaught (in promise): -1
at resolvePromise (zone-evergreen.js:797) [angular]
at :4200/polyfills.js:3942:17 [angular]
at new ZoneAwarePromise (zone-evergreen.js:876) [angular]
at ExampleComponent.ngOnInit (example.component.ts:44) [angular]
.....
We can clearly see that the unhandled error was triggered by a ngOnInit
and also see which source code file to find the offending line of code.
What happens when we use async/wait
that is reject:
public async ngOnInit() {
const p = await new Promise((resolver, reject) => reject());
}
The above generates the following stack trace:
core.js:6014 ERROR Error: Uncaught (in promise):
at resolvePromise (zone-evergreen.js:797) [angular]
at :4200/polyfills.js:3942:17 [angular]
at rejected (tslib.es6.js:71) [angular]
at Object.onInvoke (core.js:39699) [angular]
at :4200/polyfills.js:4090:36 [angular]
at Object.onInvokeTask (core.js:39680) [angular]
at drainMicroTaskQueue (zone-evergreen.js:559) [<root>]
What happened? We have no clue, because the stack trace is outside of the component.
Still, you might be tempted to use promises and just avoid using async / wait
. So let's see what happens if a promise is rejected after a setTimeout()
.
public ngOnInit() {
const p = new Promise((resolver, reject) => {
setTimeout(() => reject(), 1000);
});
}
We will get the following stack trace:
core.js:6014 ERROR Error: Uncaught (in promise): [object Undefined]
at resolvePromise (zone-evergreen.js:797) [angular]
at :4200/polyfills.js:3942:17 [angular]
at :4200/app-module.js:21450:30 [angular]
at Object.onInvokeTask (core.js:39680) [angular]
at timer (zone-evergreen.js:2650) [<root>]
Again, we've lost context here and don't know where to go to fix the bug.
Observables suffer from the same side effects of error handling, but generally the error messages are of better quality. If someone uses throwError(new Error())
the Error object will contain a stack trace, and if you're using the HttpModule
the Error object is usually a Http response object that tells you about the request.
So the moral of the story here: Catch your errors, use observables when you can and don't use async ngOnInit()
, because it will come back to haunt you as a difficult bug to find and fix.
async ngOnInit
, it is just an awkward/not recommended coding practice." – Adamsenawait
will not help, and we’d end up with two inconsistent ways (or confuse new team members). – Accrescent