What's wrong with return ….catch(err => console.error(err))
?
It returns a promise that will fulfill with undefined
after you handled the error.
On it's own, catching errors and logging them is fine at the end of a promise chain:
function main() {
const element = document.getElementById("output");
getStuff().then(result => {
element.textContent = result;
}, error => {
element.textContent = "Sorry";
element.classList.add("error");
console.error(error);
});
element.textContent = "Fetching…";
}
However if getStuff()
does catch the error itself to log it and do nothing else to handle it like providing a sensible fallback result, it leads to undefined
showing up in the page instead of "Sorry".
I've seen a lot of code that does this, why?
Historically, people were afraid of promise errors being handled nowhere which lead to them disappearing altogether - being "swallowed" by the promise. So they added .catch(console.error)
in every function to make sure that they'd notice errors in the console.
This is no longer necessary as all modern promise implementation can detect unhandled promises rejections and will fire warnings on the console.
Of course it's still necessary (or at least good practice, even if you don't expect anything to fail) to catch errors at the end of the promise chain (when you don't further return a promise).
What should I do instead?
In functions that return
a promise to their caller, don't log errors and swallow them by doing that. Just return the promise so that the caller can catch the rejection and handle the error appropriately (by logging or anything).
This also simplifies code a great lot:
function getStuff() {
return fetchStuff().then(stuff => process(stuff));
}
async function getStuff() {
const stuff = await fetchStuff();
return process(stuff);
}
If you insist on doing something with the rejection reason (logging, amending info), make sure to re-throw an error:
function getStuff() {
return fetchStuff().then(stuff =>
process(stuff)
).catch(error => {
stuffDetails.log(error);
throw new Error("something happened, see detail log");
});
}
async function getStuff() {
try {
const stuff = await fetchStuff();
return process(stuff);
} catch(error) {
stuffDetails.log(error);
throw new Error("something happened, see detail log");
}
}
Same if you are handling some of the errors:
function getStuff() {
return fetchStuff().then(stuff =>
process(stuff)
).catch(error => {
if (expected(error))
return defaultStuff;
else
throw error;
});
}
async function getStuff() {
try {
const stuff = await fetchStuff();
return process(stuff);
} catch(error) {
if (expected(error))
return defaultStuff;
else
throw error;
}
}