Suggest an editImprove this articleRefine the answer for “Errors in async functions”. Your changes go to moderation before they’re published.Approval requiredContentWhat you’re changing🇺🇸EN🇺🇦UAPreviewTitle (EN)Short answer (EN)**Any error inside an `async` function is automatically turned into a rejected promise, so you catch it either with a `try...catch` block inside the function or with `.catch()` on the promise the function returns.** `await` on a rejected promise throws an exception, so a plain `try...catch` works with asynchronous code exactly as it does with synchronous code. ```javascript async function getData() { try { const res = await fetch("https://api.example.com/data"); return await res.json(); } catch (error) { console.error("An error occurred:", error.message); } } ``` **Key point:** `try...catch` handles the error inside the function, `.catch()` handles it at the call site; with neither of them you get an unhandled promise rejection.Shown above the full answer for quick recall.Answer (EN)Image**Errors inside `async` functions are automatically turned into a rejected promise (`Promise.reject`).** That is why you handle them either with a `try...catch` block inside the function or with a `.catch()` method on the promise the function returns. ## Theory ### TL;DR - An `async` function always returns a promise, and a `throw` inside it becomes a rejection. - `await` on a rejected promise throws an exception, so an ordinary `try...catch` catches it. - `.catch()` on the function call is the second, equally valid option, convenient at the edge of the application. - One `try` can cover several `await` calls, or each `await` can get its own `try` for a targeted reaction. - A `return` from the `catch` block heals the situation: the function's promise fulfils with a default value. - In `Promise.all` one failing promise brings the whole call down, so for partial results you use `Promise.allSettled`. ### Quick example ```javascript async function getData() { try { const res = await fetch("https://no-such-domain.example/data"); const data = await res.json(); console.log(data); } catch (error) { console.error("An error occurred:", error.message); } } getData(); // Output: An error occurred: fetch failed ``` Here `try...catch` works exactly as in synchronous code, but it catches errors from every `await` inside the block. ### Why this works `await` pauses the function until the promise settles. If the promise is rejected, `await` raises an exception and control moves to `catch`. The code after a failed `await` does not run at all. ```javascript async function example() { const result = await Promise.reject("Failure!"); console.log(result); // will not run } example().catch(console.error); // Output: Failure! ``` That is the whole trick of `async/await`: a rejected promise becomes a regular exception, and a regular exception becomes a rejected promise again at the function boundary. ### A shared try block and local control Several `await` calls can be wrapped in a single `try` if all the errors are handled the same way: ```javascript async function loadUserData() { try { const user = await fetch("/api/user"); const posts = await fetch("/api/posts"); const comments = await fetch("/api/comments"); } catch (e) { console.error("Failed to load the data:", e); } } ``` If any of the operations fails, control moves to `catch` immediately, and the remaining requests never even start. Sometimes it is better to wrap individual `await` calls, when the reactions must differ: ```javascript async function example() { try { const user = await fetchUser(); } catch (e) { console.error("Failed to load the user:", e); } try { const posts = await fetchPosts(); } catch (e) { console.error("Failed to load the posts:", e); } } ``` ### Without try...catch the promise simply rejects If you do not wrap `await` in `try...catch`, the function just returns a rejected promise, which you handle with `.catch()` at the call site: ```javascript async function fail() { throw new Error("Something went wrong"); } fail().catch(err => console.error("Caught:", err.message)); // Output: Caught: Something went wrong ``` This is fully equivalent to `.catch()` handling on ordinary promises. The same `.catch()` can be attached to a single `await` when you only need to suppress the error and continue with a safe value: ```javascript async function getUser() { const res = await fetch("/api/user").catch(err => { console.error("Request failed:", err); return null; // return a safe value }); console.log("Result:", res); } getUser(); ``` And if you return a value from the `catch` block, the function fulfils successfully and that value reaches `.then()`: ```javascript async function example() { try { await Promise.reject("Failure!"); } catch (e) { console.log("Caught:", e); return "Default value"; } } example().then(result => console.log("Result:", result)); // Output: // Caught: Failure! // Result: Default value ``` ### Errors in parallel await calls If you run several operations in parallel through `Promise.all` and at least one promise rejects, the whole `Promise.all` fails with the first error: ```javascript async function parallel() { try { const [a, b] = await Promise.all([ Promise.resolve("ok"), Promise.reject("failure"), ]); } catch (e) { console.log("Caught:", e); } } parallel(); // Output: Caught: failure ``` If you need the results of every operation regardless of which ones failed, use `Promise.allSettled`: it always fulfils and returns an array of objects whose `status` is either `"fulfilled"` or `"rejected"`. ### Best practices | Situation | What to use | | --- | --- | | Handling errors inside one function | `try...catch` | | Handling errors when calling an async function | `.catch()` | | Different error types with different reactions | Several separate `try...catch` blocks | | Several parallel `await` calls | `Promise.allSettled()` instead of `Promise.all` | | Logging and rethrowing | `catch (e) { log(e); throw e; }` | ### Common mistakes - **Forgetting `await` before the call.** The function returns a promise instead of a value, and the error never reaches the `try...catch`, which has already finished. - **`return promise` instead of `return await promise` inside `try`.** Without `await` the rejection happens after control has left `try`, so the local `catch` never sees it. - **Calling an async function with no handling at all.** That gives an `unhandledrejection` in the browser and a terminated process in Node.js 15 and newer. - **Assuming `fetch` throws on 404 or 500.** `fetch` rejects only on a network failure, so the status has to be checked manually through `res.ok`. - **Catching an error and swallowing it silently.** An empty `catch` turns a failure into a success with `undefined`; either log it and `throw e`, or return an explicit default value. - **Sequential `await` calls where parallel ones are needed.** Three independent requests in three `await` statements run one after another; start them through `Promise.all` or `Promise.allSettled`. - **A `try...catch` around a `setTimeout` callback.** An exception from a timer callback goes to the global handler, not to your `catch`; the asynchronous operation has to be wrapped in a promise.For the reviewerNote to the moderator (optional)Visible only to the moderator. Helps review go faster.