Unhandled Promise Rejections
An unhandled Promise rejection occurs when a Promise is rejected and no rejection handler is attached in time to process the error. Runtimes often report these situations because they can hide failures and make asynchronous bugs difficult to diagnose.
Promises represent values that may become available later, but they can also fail. When a Promise rejects, the error should be handled with `.catch(...)` or through a `try`/`catch` block around an awaited Promise.
A rejection becomes problematic when no appropriate error handler is attached. In that case, the runtime may emit an unhandled rejection notification, warning, or error. The exact behavior depends on the environment.
Because Promise execution is asynchronous, a rejection can occur after the current call stack completes. This means traditional synchronous error handling does not automatically catch it. Error management must be part of the Promise chain.
Using `async`/`await` does not eliminate the issue. An awaited Promise that rejects without being wrapped in `try`/`catch`, or a Promise created and ignored entirely, can still lead to unhandled rejection reporting.
A common strategy is to handle errors close to where they occur, propagate meaningful failures when necessary, and ensure top-level application flows have centralized error handling for unexpected conditions.
Key points
- Rejected Promises should have a rejection handler.
- `.catch(...)` and `try`/`catch` around `await` are common handling mechanisms.
- Unhandled rejections are reported by many JavaScript runtimes.
- Ignoring a Promise can leave failures undiscovered.
- Async code requires explicit error propagation and handling.
Interview questions
What is an unhandled Promise rejection?
It occurs when a Promise rejects and no rejection handler processes the failure. The handler might be a `.catch(...)` callback or a `try`/`catch` around an awaited Promise. Many runtimes detect this condition and report it because it often indicates a bug. Unhandled rejections can cause silent failures, inconsistent state, or difficult debugging sessions.
How can you prevent unhandled rejections when using async/await?
Wrap awaited operations in `try`/`catch` when the error can be handled locally. If a function cannot resolve the problem, it can allow the error to propagate so a higher-level caller handles it. The important part is ensuring every rejection path eventually reaches code that intentionally processes the error. Logging, user feedback, and cleanup logic are often included in the handling strategy.
Why can a Promise rejection escape a synchronous try/catch block?
Promise callbacks and settlements occur asynchronously. A synchronous `try`/`catch` only observes exceptions thrown during the current execution flow. If the rejection happens later through Promise processing, the original synchronous handler is no longer active. Promise-aware handling mechanisms must be used instead.
What problems can unhandled Promise rejections cause in a frontend application?
They can leave UI state inconsistent because expected recovery logic never runs. Important operations such as data loading, analytics, or cleanup may fail silently. Developers may receive runtime warnings without enough context if errors are not logged properly. Large applications often establish centralized error reporting to detect and investigate these failures.
What is the difference between handling an error with `.catch(...)` and allowing it to propagate?
A `.catch(...)` handler consumes the rejection at that point and can recover, transform, log, or rethrow the error. Allowing propagation means a higher-level consumer becomes responsible for handling the failure. Good designs choose handling locations deliberately rather than catching everything immediately. The decision depends on ownership, recovery capability, and application architecture.