Why try...catch Misses Promise Errors and How async/await Solves It
This article explains why a traditional try...catch block cannot catch asynchronous Promise rejections in JavaScript, illustrates the sync‑async mismatch with a food‑delivery analogy, and shows how using async/await or .catch() correctly handles such errors.
In JavaScript, try...catch is our go‑to error‑handling tool, but when dealing with Promises it can behave unexpectedly.
try {
// Assume this API request will fail
fetch('https://non-existent-url.com/api');
console.log('Request sent');
} catch (error) {
// Will this catch run?
console.log('Caught error!', error);
}
// Console output:
// Request sent
// Uncaught (in promise) TypeError: Failed to fetchThe catch block never runs; the error appears as Uncaught (in promise). This is not a bug in try...catch but a misunderstanding of synchronous versus asynchronous execution.
Core reason: try...catch is synchronous, Promise is asynchronous
Think of placing an order (creating a Promise) as ordering food delivery. The try...catch acts like a guard at your door who only watches the moment you place the order. The order succeeds instantly (synchronous), the guard leaves, and later the delivery crashes (Promise rejects). The guard cannot catch that later error.
The code inside try { ... } runs synchronously.
fetch(...) returns a Promise immediately. No error is thrown at this point, so the try block finishes without triggering catch.
The actual network error occurs later, turning the Promise into a rejected state. This belongs to the asynchronous world, and the already‑finished try...catch cannot reach it.
Correct approach: use async/await
Mark the function with async. The await keyword pauses the async function until the Promise settles, then re‑throws any rejection as a synchronous error that try...catch can capture.
Transformed code (illustrated below) catches the error perfectly:
Workflow of async/await:
The function is declared with async, indicating it returns a Promise. await sits before fetch(...); execution pauses here without blocking the whole program.
It waits for the Promise returned by fetch to settle.
If the Promise is rejected (e.g., network failure), await throws the error, which is then caught by the surrounding try...catch.
The thrown error is caught inside the same try block, allowing graceful handling.
Don’t forget .catch() as an alternative
Before async/await, we handled Promise errors with the chainable .catch() method, which is also effective.
fetch('https://non-existent-url.com/api')
.then(response => {
if (!response.ok) {
// Manually throw an error to be caught below
throw new Error('Network response not ok');
}
return response.json();
})
.then(data => {
console.log('Request succeeded:', data);
})
.catch(error => {
// Any error in the .then() chain lands here
console.log('Caught error in .catch():', error);
});This pattern keeps a clear “success path” ( .then) and a distinct “failure path” ( .catch).
Signed-in readers can open the original source through BestHub's protected redirect.
This article has been distilled and summarized from source material, then republished for learning and reference. If you believe it infringes your rights, please contactand we will review it promptly.
JavaScript
Provides JavaScript enthusiasts with tutorials and experience sharing on web front‑end technologies, including JavaScript, Node.js, Deno, Vue.js, React, Angular, HTML5, CSS3, and more.
How this landed with the community
Was this worth your time?
0 Comments
Thoughtful readers leave field notes, pushback, and hard-won operational detail here.
