JavaScript
Completed
-
Section 1: Getting Started
-
Section 2: Core Syntax and Types
-
Section 3: Strings and Numbers in Depth
-
Section 4: Control Flow
-
Section 5: Functions
-
Section 6: Objects and Arrays
-
Section 7: Maps, Sets, and Symbols
-
Section 8: Asynchronous JavaScript
-
Section 9: Object-Oriented and Prototypes
-
Section 10: The DOM
-
Section 11: Browser APIs
-
Section 12: Modern JavaScript (ES2015-ES2025)
-
Section 13: Functional Programming Patterns
-
Section 14: Error Handling and Debugging
-
Section 15: Testing
-
Section 16: Accessibility for JavaScript Developers
-
Section 17: Internationalization and Localization
-
Section 18: Performance
-
Section 19: Node.js Fundamentals
-
Section 20: Regular Expressions
-
Section 21: Design Patterns in JavaScript
-
Section 22: Security Basics
-
Section 23: Data Structures and Algorithms in JavaScript
-
Section 24: Practical Projects
-
Section 25: More Advanced Async Patterns
-
Section 26: More Object and Class Practice
-
Section 27: Working with Dates and Internationalization
-
Section 28: Web Components
-
Section 29: More DOM and Browser Practice
-
Section 30: Build Tooling for Vanilla JavaScript
-
Section 31: More Practice Projects
-
Section 32: Interview and Algorithm Practice
-
Section 33: Error Objects (MDN Reference)
-
Section 34: TypedArrays and Binary Data
-
Section 35: Reflection and Metaprogramming (MDN Reference)
-
Section 36: More Global Functions (MDN Reference)
237: AggregateError for Multiple Errors
I was working on a project recently where we had to fetch a configuration file from three different backup servers. The goal was simple: as long as one of them responded, we were good to go. If all three failed, we needed to log exactly why every single one failed so the DevOps team could figure out if it was a network partition or a credential issue.
The "First Failure" Trap
My first instinct was to use Promise.all(), but as you probably remember, that's a "fail-fast" mechanism. The second one of those servers blinked, the whole thing crashed, and I lost the context of the other requests. That's not what I wanted. I wanted to wait until I knew for sure that none of them worked.
So, I switched to Promise.any(). This is the perfect tool for this because it only rejects if every single promise in the array rejects. I set up a quick test script to simulate three different types of failures:
const fetchFromMirror = (name, shouldFail, errorMsg) => {
return new Promise((_, reject) => {
setTimeout(() => reject(new Error(`${name}: ${errorMsg}`)), 100);
});
};
async function loadConfig() {
try {
await Promise.any([
fetchFromMirror('Mirror-A', true, 'Connection Timeout'),
fetchFromMirror('Mirror-B', true, '404 Not Found'),
fetchFromMirror('Mirror-C', true, 'Invalid SSL Certificate'),
]);
} catch (err) {
console.log('Something went wrong:', err);
}
}
loadConfig();
Wait, where are the other errors?
When I ran this, the output was a bit surprising. I expected a list of errors, but I just got a single object logged to the console. If I look closer at the error object, it's not a standard Error; it's an AggregateError.
At first, it looks like a normal error message, but there's a catch. If I try to access err.message, it just says "All promises were rejected". That's frustrating. I don't just want to know that they all failed; I want the specifics of how they failed. I need those timeout and SSL messages I wrote into the promises.
Unpacking the Aggregate
I did a quick dive into the object and found the secret: the errors property. Unlike a standard error, an AggregateError carries an array of all the individual errors that triggered it. It's essentially a container.
Let's adjust the catch block to actually make use of this. I'll use instanceof to make sure we're actually dealing with an AggregateError before we try to iterate over it, just to keep the code robust.
async function loadConfig() {
try {
await Promise.any([
fetchFromMirror('Mirror-A', true, 'Connection Timeout'),
fetchFromMirror('Mirror-B', true, '404 Not Found'),
fetchFromMirror('Mirror-C', true, 'Invalid SSL Certificate'),
]);
} catch (err) {
if (err instanceof AggregateError) {
console.log('All mirrors failed. Detailed breakdown:');
err.errors.forEach((e, index) => {
console.error(`Error ${index + 1}: ${e.message}`);
});
} else {
console.error('An unexpected error occurred:', err);
}
}
}
loadConfig();
Now we're getting somewhere. Instead of a generic failure message, I can see exactly which mirror had the SSL issue and which one timed out. You can actually throw these yourself too, if you're performing a series of validations and want to report every single validation error back to the user at once, rather than making them fix one error, submit, and find the next one.
📋 Practical Task
Build a Batch User-Validation Reporter
You are building a registration form validator. Instead of throwing an error at the first invalid field, you want to collect all validation errors and throw them as a single AggregateError so the user can fix everything in one go.
Create a function validateUser(user) that checks for the following:
user.usernamemust be at least 3 characters long.user.emailmust contain an "@" symbol.user.agemust be 18 or older.
If any of these fail, push a new Error object into an array. If the array is not empty at the end, throw a new AggregateError containing that array of errors. Wrap the function call in a try...catch block that detects the AggregateError and logs each individual validation failure to the console.
There are no comments for now.