A rejected promise inside an async function behaves like a thrown exception, which means error handling stops being a .catch() bolted onto the end of a chain and becomes ordinary control flow.
async function place(order) {
try {
const receipt = await charge(order);
await notify(order, receipt);
} catch (e) {
report(e);
throw e; // or the caller cannot tell it failed
} finally {
releaseLock(order);
}
}
The finally works too, which the promise API only gained recently. The line to be deliberate about is the re-throw: swallowing the error here makes the function resolve successfully, so the caller proceeds as though the charge happened. Catching without re-throwing or returning a sentinel is the async equivalent of an empty catch block.