Reputation: 77285
The following code is an MCVE of my problem:
class MyApp {
public async crash(): Promise<number> {
throw new Error("Hello Error");
}
public async chain(promise: Promise<number>) {
await this.delay(10000);
return promise;
}
private async delay(ms: number): Promise<void> {
return new Promise<void>( resolve => setTimeout(resolve, ms) );
}
}
const myApp = new MyApp();
const a = async () => {
try {
const foo = await myApp.chain(myApp.crash());
} catch (e) {
console.log("Hello Catch");
}
};
a();
I have a Promise<>
, that sooner or later will reject. I have a handler that will await
said promise. However, if the rejection is faster then my code leading to the await
call, some global error handler will kick in and end my app, telling me I had an uncaught error. Funny side effect: if I wait long enough (10s in my example), it will magically recover and remember that there was an await
and a catch
block and actually do what's in the catch.
But for those 10 seconds, my script is in the "we are all gonna die!" mode. Some global error handler kicks in, my app is shut down.
What am I doing wrong? Isn't the whole point of being async
that I don't have to handle it right now? Why is there even a global error handler?
My real world case is this:
public async showWait<T>(promise: Promise<T>): Promise<T> {
await loadingSpinner.present();
try {
return await promise;
}
finally {
await loadingSpinner.dismiss();
}
}
If the task to execute crashes faster than I can show a spinner, my whole app will crash. And before you jump in and tell me to add a catch block, please see the MCVE, that's not the problem. I do catch the exception from the await
higher up the call chain, but the crash happens before the code even reaches the await
.
Upvotes: 2
Views: 2918
Reputation: 664256
The problem is that you are constructing a promise and not doing anything with it (not installing an error handler!) while waiting for some other promise in the mean time:
let promise = crash();
await loadingSpinner.present();
await promise;
This is a well-known antipattern: Waiting for more than one concurrent await operation
In general your methods should accept values not promises, but in this case it might be warranted if you want to wrap the promise. You still need to immediately handle it though. The proper solution would be
public async showWait<T>(promise: Promise<T>): Promise<T> {
try {
const [, val] = await Promise.all([
loadingSpinner.present(),
promise,
]);
return val;
} finally {
await loadingSpinner.dismiss();
}
}
Upvotes: 4