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)
59: Promises in Depth
Imagine you're at a busy burger joint. You place your order at the counter, pay your money, and instead of standing there staring at the cook until the burger is done, the cashier hands you one of those little buzzing pagers.
That pager is a Promise. It’s not the burger itself, but it’s a guarantee that you will eventually receive something. Right now, the pager is in a "pending" state. Eventually, one of two things will happen: the pager buzzes, meaning your food is ready (the promise is fulfilled), or the manager comes over and tells you they've run out of patties (the promise is rejected).
In JavaScript, we use Promises to handle operations that take time—like fetching data from a server or reading a large file—without freezing the entire browser while we wait.
The Agreement and the Outcome
When you create a Promise, you're defining exactly what should happen when the task finishes. You provide an "executor" function that takes two arguments: resolve and reject. I like to think of these as the "success" and "failure" buttons.
const checkInventory = new Promise((resolve, reject) => {
const stockAvailable = true; // Imagine this is a database call
setTimeout(() => {
if (stockAvailable) {
resolve("Items are in stock!"); // This triggers .then()
} else {
reject("Out of stock!"); // This triggers .catch()
}
}, 1500);
});
Notice how I used setTimeout? That's just to simulate the delay of a real network request. The key here is that checkInventory is now an object representing a future value. It doesn't hold the string "Items are in stock!" immediately; it holds the promise of that string.
Building a Pipeline of Events
The real power of Promises isn't just waiting for one thing; it's what you do once it arrives. We use .then() to schedule the next step. But here is the part where people often get confused: .then() actually returns a new promise. This allows us to chain operations together like a conveyor belt.
checkInventory
.then((message) => {
console.log(message);
return "Processing payment..."; // Pass this to the next .then()
})
.then((paymentStatus) => {
console.log(paymentStatus);
return "Order shipped!";
})
.then((shippingStatus) => {
console.log(shippingStatus);
});
If any of those .then() blocks return a value, that value is wrapped in a resolved promise and passed down the line. If you return another Promise inside a .then(), the chain will wait for that promise to finish before moving to the next step. It's a clean way to avoid the "callback hell" we used to deal with years ago.
When Things Go South
In the real world, APIs fail, servers crash, and users lose internet. If a promise is rejected, it skips all subsequent .then() blocks until it finds a .catch(). I always recommend putting a .catch()` at the very end of your chain to act as a safety net.
fetchUserProfile(userId)
.then(user => fetchUserPosts(user.id))
.then(posts => console.log("Got posts:", posts))
.catch(error => {
console.error("Something went wrong in the chain:", error);
})
.finally(() => {
console.log("Loading spinner hidden."); // Runs regardless of success or failure
});
The .finally() block is a lifesaver. I use it almost every time I make an API call to hide a loading spinner or close a database connection, because I don't want to duplicate that logic in both the success and error handlers.
Waiting for the Whole Crew
Sometimes you need several things to happen before you can move forward. Imagine you're building a dashboard that needs data from three different APIs: User Info, Recent Orders, and Notifications. You don't want to wait for them one by one; you want them to run in parallel.
This is where Promise.all() comes in. It takes an array of promises and returns a single promise that resolves only when all of them have succeeded.
const userPromise = fetchUser();
const ordersPromise = fetchOrders();
const notifyPromise = fetchNotifications();
Promise.all([userPromise, ordersPromise, notifyPromise])
.then(([user, orders, notifications]) => {
console.log("All data received! Rendering dashboard...");
})
.catch(err => {
console.error("One of the requests failed, so the whole dashboard failed:", err);
});
Just a heads-up: Promise.all is "all or nothing." If even one promise in that array rejects, the whole thing crashes straight into the .catch(). If you need to know the result of every single promise regardless of whether some failed, you'll want to look into Promise.allSettled(), but for most strict dependencies, Promise.all is your best bet.
📋 Practical Task
Exercise: The Multi-Source Weather Dashboard Aggregator
You are building a weather dashboard that aggregates data from three different mock services. Your goal is to fetch data from all three simultaneously and handle the results correctly.
Requirements:
- Create three functions:
getTemperature(),getHumidity(), andgetWindSpeed(). Each should return a Promise that resolves after a random delay (between 500ms and 2000ms) with a mock value (e.g., "22°C", "45%", "12km/h"). - To make it realistic, make
getWindSpeed()randomly fail (reject) 30% of the time. - Use
Promise.all()to call all three functions. - If all three succeed, log a formatted string:
"Weather Report: Temp: 22°C, Humidity: 45%, Wind: 12km/h". - If any of them fail, log:
"Error: Could not retrieve complete weather data." - Ensure a final message
"Request sequence complete."is logged regardless of the outcome.
There are no comments for now.