How is that cleaner than this?
async function updateUser() {
const user = await getUser();
.
.
.
const result = await user.save();
return result;
// or
// return user.save();
}
If it errors, the caller of updateUser()
knows if it should handle it and what it should do. If the caller doesn't know what to, then the caller will return a promise as well (either by virtue of being async
and await
ing the call to updateUser()
or by returning the promise returned by updateUser()
), and it doesn't need to perform error handling.
Eventually you'll reach a frame in your call stack that knows what to do with an error, in which case it performs error handling for any possible rejected promises awaited. If not, then you've probably reached a callback from a framework or the entry point of your program which will likely propagate the error as an unhandled promise rejection.
Before promises, there were callbacks, and it took a lot of discipline not to write everything like this:
function updateUser(callback) {
getUser(function (error, user) {
if (error) {
return callback(error);
}
user.save(function (error, result) {
if (error) {
return callback(error);
}
callback(null, result);
// or
// callback(error, result);
});
// or
// user.save(callback);
});
}
Notice that even callbacks which don't know what to do with the error have to explicitly perform error handling anyway, this is one of the reasons why promises suck so much less. One might point out that you can make it cleaner by extracting each callback into a named function to flatten out the recursion visually, but even then, each callback still has to perform error handling.
Then promises came along and the problem of explicit error handling at every frame eventually went away:
function updateUser() {
return getUser().then(user => {
return user.save();
}).then(result => {
return result;
});
// exposition only
}