Unhandled Promise Rejections in JavaScript: Causes, Consequences, and Solutions
Step 1: Understanding Promises in JavaScript
A Promise in JavaScript is an object that represents the eventual completion (or failure) of an asynchronous operation. It can be in one of three states:
- Pending: The initial state, neither fulfilled nor rejected.
- Fulfilled: The operation was successful.
- Rejected: The operation failed.
Basic Syntax of a Promise
const myPromise = new Promise((resolve, reject) => {
let success = true; // Change to false to test rejection
if (success) {
resolve("Operation was successful!");
} else {
reject("Something went wrong!");
}
});
// Handling the Promise
myPromise
.then(result => console.log(result)) // Handles success
.catch(error => console.error(error)); // Handles failure
✅ This correctly handles both success and failure cases using .then()
and .catch()
.
Step 2: What is an Unhandled Promise Rejection?
An Unhandled Promise Rejection occurs when a Promise rejects, but there is no .catch()
or error-handling mechanism in place to handle the rejection.
Example of an Unhandled Promise Rejection
const myPromise = new Promise((resolve, reject) => {
reject("Error occurred!"); // The promise is rejected
});
// No `.catch()` is used, so the rejection is unhandled!
🚨 This will cause an UnhandledPromiseRejectionWarning in Node.js or an error in the browser’s developer console.
Step 3: Why Are Unhandled Promise Rejections a Problem?
- Silent Failures: If an error is not handled, it can cause unexpected behavior without visible errors.
- Hard to Debug: When unhandled, errors might surface later in unrelated parts of your application.
- Crashes in Node.js: Starting from Node.js v15, unhandled promise rejections terminate the process.
Step 4: Real-World Scenarios of Unhandled Promise Rejections
1. Forgetting to Attach .catch()
fetch("https://api.example.com/data")
.then(response => response.json()) // ✅ Success case
// ❌ No `.catch()` to handle rejection
✅ Fix: Always Attach .catch()
fetch("https://api.example.com/data")
.then(response => response.json())
.catch(error => console.error("Fetch failed:", error));
2. Using async/await
Without Try-Catch
async function fetchData() {
let response = await fetch("https://api.example.com/data"); // ❌ Can fail!
let data = await response.json();
console.log(data);
}
fetchData(); // If fetch fails, the rejection is unhandled!
🚨 This will result in an Unhandled Promise Rejection if fetch()
fails.
✅ Fix: Use Try-Catch Block
async function fetchData() {
try {
let response = await fetch("https://api.example.com/data");
let data = await response.json();
console.log(data);
} catch (error) {
console.error("Error fetching data:", error);
}
}
fetchData();
3. Returning a Rejected Promise Without Handling
function riskyOperation() {
return new Promise((resolve, reject) => {
reject("Operation failed!");
});
}
riskyOperation(); // ❌ No `.catch()` attached
✅ Fix: Attach .catch()
or use async/await
with try-catch
riskyOperation().catch(error => console.error("Handled:", error));
or
async function handleRiskyOperation() {
try {
await riskyOperation();
} catch (error) {
console.error("Caught error:", error);
}
}
handleRiskyOperation();
Step 5: Handling Unhandled Promise Rejections Globally
1. Using process.on("unhandledRejection")
in Node.js
process.on("unhandledRejection", (reason, promise) => {
console.error("Unhandled Rejection:", reason);
});
✅ This ensures that even if a Promise rejection is not handled locally, it gets logged and does not crash the application.
2. Using window.addEventListener("unhandledrejection")
in Browsers
window.addEventListener("unhandledrejection", event => {
console.error("Unhandled promise rejection:", event.reason);
});
✅ This captures all unhandled rejections in client-side JavaScript.
Step 6: Best Practices to Avoid Unhandled Promise Rejections
Best Practice | Example |
---|---|
Always attach .catch() to promises | myPromise.catch(error => console.error(error)); |
Use try-catch in async functions | try { await fetchData(); } catch (error) { console.error(error); } |
Use process.on("unhandledRejection") in Node.js | process.on("unhandledRejection", reason => console.error(reason)); |
Use window.addEventListener("unhandledrejection") in browsers | window.addEventListener("unhandledrejection", event => console.error(event.reason)); |
Use Promise.allSettled() for multiple promises | Promise.allSettled([p1, p2, p3]).then(results => console.log(results)); |
Step 7: Summary
✅ Unhandled Promise Rejections occur when a rejected promise is not properly handled.
🚨 This can lead to silent failures, crashes, and hard-to-debug issues.
💡 Always use .catch()
, try-catch blocks, and global rejection handlers (process.on()
and window.addEventListener()
).
Would you like a debugging checklist or sample logging utility for tracking Promise rejections?