When you convert a callback to a Promise, make sure that you also handle the error case. Without that, the Promise is never resolved and an await
waits forever.
For example, the gapi.load
provides separate callbacks for results and errors:
await new Promise((res, rej) => {
gapi.load("client:auth2", {callback: res, onerror: rej});
});
If you don't attach the rej
callback to the onerror
handler, errors won't propagate through the chain. This leads to hard-to-debug problems and components that "just stops".
When you use try..catch
or try..finally
in an async function, make sure that you use await
too. For example, this code works fine when the inner Promise resolves:
const fn = async () => {
return "result";
}
const outer = async () => {
try {
return fn(); // <= missing await!
}catch(e) {
console.log(e.message);
}
}
await outer(); // result
But the try..catch
won't handle exceptions:
const fn = async () => {
throw new Error("error");
}
const outer = async () => {
try {
return fn(); // <= missing await!
}catch(e) {
console.log(e.message);
}
}
await outer();
// exception is thrown!
This is because exceptions are thrown from the await
and while returning a Promise from an async function flattens the result, it will be returned without waiting for it. And without waiting, an exception is not thrown for the try..catch
but when the caller uses await
.
const fn = async () => {
throw new Error("error");
}
const outer = async () => {
try {
return await fn();
}catch(e) {
console.log(e.message); // error
}
}
await outer();
// no exception here
When a Promise is resolved it does not necessarily mean that everything is alright. For example, the fetch
rejects when there is a problem with the connection but it resolves even if the response status returns an error.
When the call can not establish a connection, the Promise rejects: