try/catch around await catches a rejection

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.