Comprehensive Guide to Error Handling with JavaScript Promises, async/await, and Node.js
This tutorial explains how to handle errors in JavaScript using Promise.reject/resolve, then/catch/finally, Promise.all, Promise.any, Promise.race, Promise.allSettled, async/await, async generators, and Node.js callback and EventEmitter patterns, providing clear code examples for each approach.
Using Promise to Handle Errors
To demonstrate Promise handling, we start with a simple function that throws an exception when the argument is not a string.
function toUppercase(string) {
if (typeof string !== "string") {
throw TypeError("Wrong type given, expected a string");
}
return string.toUpperCase();
}
toUppercase(4);Instead of throwing, we can return Promise.reject or Promise.resolve:
function toUppercase(string) {
if (typeof string !== "string") {
return Promise.reject(TypeError("Wrong type given, expected a string"));
}
const result = string.toUpperCase();
return Promise.resolve(result);
}Because the function now returns a Promise, we can use then to receive the result or catch to capture the error.
toUppercase(99)
.then(result => result)
.catch(error => console.error(error.message));The output is the error message "Wrong type given, expected a string".
Besides then and catch, Promise also provides finally, which works like the finally block in a try/catch statement.
toUppercase(99)
.then(result => result)
.catch(error => console.error(error.message))
.finally(() => console.log("Run baby, run"));Promise, Error, and Throw
Promise.rejectcan be used to throw errors conveniently:
Promise.reject(TypeError("Wrong type given, expected a string"));Alternatively, throwing an exception inside a Promise chain also rejects the Promise.
Promise.resolve("A string").then(value => {
if (typeof value === "string") {
throw TypeError("Expected a number!");
}
});We catch the error with catch:
Promise.resolve("A string")
.then(value => {
if (typeof value === "string") {
throw TypeError("Expected a number!");
}
})
.catch(reason => console.log(reason.message));This pattern is common with fetch:
fetch("https://example-dev/api/")
.then(response => {
if (!response.ok) {
throw Error(response.statusText);
}
return response.json();
})
.then(json => console.log(json));Handling Errors in Timers
Exceptions thrown inside a timer callback cannot be caught with a surrounding try/catch.
function failAfterOneSecond() {
setTimeout(() => {
throw Error("Something went wrong!");
}, 1000);
}
// DOES NOT WORK
try {
failAfterOneSecond();
} catch (error) {
console.error(error.message);
}The solution is to wrap the asynchronous work in a Promise and reject it:
function failAfterOneSecond() {
return new Promise((_, reject) => {
setTimeout(() => {
reject(Error("Something went wrong!"));
}, 1000);
});
}
failAfterOneSecond().catch(reason => console.error(reason.message));Using Promise.all for Error Handling
Promise.all(iterable)resolves when all promises are fulfilled; if any promise rejects, the returned promise rejects with the first rejection reason.
const promise1 = Promise.resolve("All good!");
const promise2 = Promise.reject(Error("No good, sorry!"));
const promise3 = Promise.reject(Error("Bad day ..."));
Promise.all([promise1, promise2, promise3])
.then(results => console.log(results))
.catch(error => console.error(error.message));
// Output: No good, sorry!Regardless of success or failure, finally runs:
Promise.all([promise1, promise2, promise3])
.then(results => console.log(results))
.catch(error => console.error(error.message))
.finally(() => console.log("Always runs!"));Using Promise.any for Error Handling
Promise.any()resolves as soon as any promise fulfills; if all reject, it rejects with an AggregateError containing all rejection reasons.
const promise1 = Promise.reject(Error("No good, sorry!"));
const promise2 = Promise.reject(Error("Bad day ..."));
Promise.any([promise1, promise2])
.then(result => console.log(result))
.catch(error => console.error(error))
.finally(() => console.log("Always runs!"));Using Promise.race for Error Handling
Promise.race(iterable)settles as soon as the first promise settles, whether fulfilled or rejected.
const promise1 = Promise.resolve("The first!");
const promise2 = Promise.resolve("The second!");
Promise.race([promise1, promise2]).then(result => console.log(result));
// Output: The first!If a rejection appears first, the race rejects:
const promise1 = Promise.resolve("The first!");
const rejection = Promise.reject(Error("Ouch!"));
const promise2 = Promise.resolve("The second!");
Promise.race([rejection, promise1, promise2])
.then(result => console.log(result))
.catch(error => console.error(error.message));
// Output: Ouch!Using Promise.allSettled for Error Handling
Promise.allSettled()returns a promise that resolves after all input promises settle, providing an array of objects describing each outcome.
const promise1 = Promise.resolve("Good!");
const promise2 = Promise.reject(Error("No good, sorry!"));
Promise.allSettled([promise1, promise2])
.then(results => console.log(results))
.finally(() => console.log("Always runs!"));Using async/await for Error Handling
Marking a function with async makes it return a Promise, allowing the use of then, catch, and finally after the call.
async function toUppercase(string) {
if (typeof string !== "string") {
throw TypeError("Wrong type given, expected a string");
}
return string.toUpperCase();
}
toUppercase("abc")
.then(result => console.log(result))
.catch(error => console.error(error.message))
.finally(() => console.log("Always runs!"));Within an async function, traditional try/catch/finally works as well:
async function consumer() {
try {
await toUppercase(98);
} catch (error) {
console.error(error.message);
} finally {
console.log("Always runs!");
}
}
consumer();Using async generators for Error Handling
Async generators produce Promises for each yield. Throwing inside the generator rejects the Promise.
async function* asyncGenerator() {
yield 33;
yield 99;
throw Error("Something went wrong!"); // Promise.reject
}
const go = asyncGenerator();
go.next().then(value => console.log(value));
go.next().then(value => console.log(value));
go.next().catch(reason => console.error(reason.message));They can also be consumed with for await...of and wrapped in try/catch:
async function consumer() {
try {
for await (const value of asyncGenerator()) {
console.log(value);
}
} catch (error) {
console.error(error.message);
}
}
consumer();Node.js Synchronous Error Handling
Synchronous errors in Node.js are handled with the usual try/catch/finally blocks.
Node.js Asynchronous Error Handling: Callback Pattern
Asynchronous Node APIs accept a callback where the first argument is an error object.
const { readFile } = require("fs");
function readDataset(path) {
readFile(path, { encoding: "utf8" }, function(error, data) {
if (error) console.error(error);
// do stuff with the data
});
}Errors can be thrown, but they will crash the process; instead, forward the error to an error‑handling callback.
function errorHandler(error) {
console.error(error.message);
// log or report the error
}
function readDataset(path) {
readFile(path, { encoding: "utf8" }, function(error, data) {
if (error) return errorHandler(error);
// do stuff with the data
});
}Node.js Asynchronous Error Handling: Event Emitters
Many Node modules inherit from EventEmitter, which emits an error event when something goes wrong.
const net = require("net");
const server = net.createServer().listen(80, "127.0.0.1");
server.on("listening", function() {
console.log("Server listening!");
});
server.on("connection", function(socket) {
console.log("Client connected!");
socket.end("Hello client!");
});
server.on("error", function(error) {
console.error(error.message);
});Summary
This guide covered various JavaScript error‑handling techniques, from simple synchronous try/catch to advanced asynchronous patterns using Promises, async/await, async generators, and Node.js callback and EventEmitter approaches.
Synchronous errors are the easiest to catch; asynchronous errors require careful use of Promise methods or async/await constructs.
Modern browser APIs favor Promise‑based patterns, making then/catch/finally or try/catch with async/await the preferred ways to handle exceptions.
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.
Sohu Tech Products
A knowledge-sharing platform for Sohu's technology products. As a leading Chinese internet brand with media, video, search, and gaming services and over 700 million users, Sohu continuously drives tech innovation and practice. We’ll share practical insights and tech news here.
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.
