Choose who owns the failure
An asynchronous helper should either recover with a useful value or reject so its caller can decide what to show. Mixing both approaches without a contract produces confusing behavior: a helper logs an error and returns undefined, while the screen mistakes the fulfilled promise for a successful save. Decide what successful completion means before adding catch handlers.
Return every dependent operation
Consider a report workflow that loads records, creates a file and saves it. If a then callback starts file creation but does not return its promise, the outer chain cannot wait for that step or observe its rejection. Return the next operation, or await it inside an async function, so the workflow remains one inspectable chain from beginning to end.
Recover only where recovery is meaningful
A failed optional avatar request might reasonably use an initials image. A failed invoice save should not silently substitute an empty object. Make fallbacks specific to the feature and visible when they affect what the user can do. If a catch handler cannot restore the promised result, rethrow the error rather than allowing accidental success.
Separate feedback from diagnostics
A user needs an explanation such as The report could not be generated, plus a sensible next action. A developer may need the failing stage and request identifier. Do not expose access tokens, raw personal records or internal stack traces in the interface. Keep diagnostic context useful without turning ordinary failure handling into an unintended data disclosure.
Use cleanup without changing the outcome
Put pending-state cleanup in finally when the action should stop looking busy regardless of success or failure. A finally handler is not the place to invent a replacement result. Also avoid returning from finally in a way that hides a thrown error. Keep result assignment in the success path and error feedback in the failure path.
Exercise each failure boundary
Test the loader rejecting, the transformer throwing and the download step failing independently. Confirm that the busy indicator clears, the previous valid result remains understandable, and repeated attempts still work. Watch for unhandled promise rejections in the console. A caught error is only handled properly when the application reaches a deliberate, useful state afterward.
Further reading
Put the idea to work.
Explore free browser tools for images, text and developer workflows.