Catch an Asynchronous Failure
Catch an Asynchronous Failure
A try block catches an awaited rejection because await turns that rejection into a thrown error at the continuation. Without await, returning a rejected promise can leave the local catch bypassed.
Before this topic, review Fetch and Validate a Response if its terms are unfamiliar.
What you’ll learn
- Explain catch an asynchronous failure through the school example.
- Trace the shown operations and distinguish their result from displayed output.
- Make the practice change and explain its consequence: The awaited rejection is still caught, but its reason now describes invalid data.
Work through the school example
This is JavaScript using language features and the host console. Read the initial values, then follow each operation to its displayed result. A declaration names a value, a function call performs an operation, and console.log displays its arguments.
JavaScript · school.js · complete example
async function report() {
try {
await Promise.reject(new Error("Missing mark"));
} catch (error) {
console.log(error.message);
}
}
report();
Expected output
Missing markRun in JavaScript compiler →Understanding Error Handling in Asynchronous JavaScript
When writing asynchronous code in JavaScript, such as using Promises or async/await, it's important to handle errors properly. Errors in async code behave differently compared to synchronous code, so we must be cautious and deliberate in our handling strategy.
Why Error Handling is Important in Async Code?
- Network requests can fail due to connectivity or server issues.
- Third-party APIs can return errors or unexpected data formats.
- File operations or database queries may timeout or reject.
Example 1: Error Handling in Promises using .catch()
Let’s say we have a promise that may reject due to some error. We can handle it using the .catch() method.
function getData() {
return new Promise((resolve, reject) => {
const success = false; // Change this to true to test success
if (success) {
resolve("Data fetched successfully!");
} else {
reject("Failed to fetch data.");
}
});
}
getData()
.then(result => console.log(result))
.catch(error => console.error("Error:", error));
Error: Failed to fetch data.
How does .catch() work?
The .catch() method is attached to the end of a promise chain. It catches any error that is thrown or passed to reject() in the chain.
Question:
What happens if we forget to add .catch()?
The error will be unhandled, and in modern environments, it might log a warning or throw a UnhandledPromiseRejectionWarning.
Example 2: Error Handling with async/await and try-catch
With async/await, we can write asynchronous code in a synchronous style. To catch errors, we wrap the code in a try-catch block.
async function fetchUserData() {
try {
const user = await getData(); // using the same getData function from above
console.log(user);
} catch (err) {
console.error("Caught inside async/await:", err);
}
}
fetchUserData();
Caught inside async/await: Failed to fetch data.
Why use try-catch with async/await?
Because await pauses execution until the promise resolves or rejects. If it rejects, control jumps to the catch block.
Question:
Can I mix async/await with .catch()?
Yes, although it is not common. Usually, you'd prefer try-catch with await. But technically, you can do this:
async function fetchData() {
await getData()
.then(data => console.log(data))
.catch(err => console.error("Mixed handling:", err));
}
fetchData();
Example 3: Global Promise Rejection Handler
Sometimes you want to catch unhandled promise rejections globally, especially in larger apps.
process.on("unhandledRejection", (reason, promise) => {
console.log("Unhandled Rejection at:", promise, "reason:", reason);
});
This is useful in Node.js environments for logging or cleanup.
Best Practices
- Always handle errors in your promises and async functions.
- Use
try-catchwithawaitfor more readable code. - For multiple async operations, handle each individually or use
Promise.allSettled(). - Log errors with enough context to debug later.
Conclusion
Asynchronous JavaScript is powerful, but only if we handle errors properly. Whether you use promises or async/await, error handling ensures your application behaves predictably and avoids silent failures.
Recognize the important boundary
A catch should recover, report or rethrow intentionally. Silently replacing every failure with success hides defects.
Try a small variation
JavaScript · original source for practice
async function report() {
try {
await Promise.reject(new Error("Missing mark"));
} catch (error) {
console.log(error.message);
}
}
report();
Your task: Replace Missing mark with Invalid mark. Predict the result before running the edited program.
The source is repeated here. If the edit targets the HTML rather than the script, use the markup repeated below; the worked answer also repeats the changed markup.
Worked answer and reasoning
The awaited rejection is still caught, but its reason now describes invalid data.
JavaScript · complete worked variation
async function report() {
try {
await Promise.reject(new Error("Invalid mark"));
} catch (error) {
console.log(error.message);
}
}
report();
Expected output
Invalid markRun in JavaScript compiler →Predict the original program
JavaScript · original program for this question
async function report() {
try {
await Promise.reject(new Error("Missing mark"));
} catch (error) {
console.log(error.message);
}
}
report();
Keep the useful idea
A try block catches an awaited rejection because await turns that rejection into a thrown error at the continuation. Without await, returning a rejected promise can leave the local catch bypassed.
Check your variation against this explanation: The awaited rejection is still caught, but its reason now describes invalid data.
Use the JavaScript array reference library for individual array operations and boundary tests to check a rule independently.
Reaching the recap and completing the quizzes are separate milestones.