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)
64: Fetching Data with the Fetch API
I want to show you how we actually get data into our apps from the outside world. For a long time, we had to deal with XMLHttpRequest, which was—to put it politely—a nightmare to write. Now we have the Fetch API. It's cleaner, but there's a specific rhythm to it that trips everyone up the first time.
Let's try to grab a fake blog post from JSONPlaceholder, a free API used for testing. I'll start with the simplest possible call.
Wait, where's my data?
const data = fetch('https://jsonplaceholder.typicode.com/posts/1');
console.log(data);
If you run that, you're probably expecting to see a post object in the console. Instead, you'll see something like Promise {<pending>}. This is the classic "first-time fetch" mistake. I did it for weeks when I started.
The fetch function doesn't return the data immediately because network requests take time. It returns a Promise—essentially a placeholder that says, "I'll let you know when the server actually responds." To get the actual value, we need to wait for that promise to resolve.
fetch('https://jsonplaceholder.typicode.com/posts/1')
.then(response => {
console.log(response);
});
Unwrapping the Response
Okay, we've moved past the pending promise. But look at the console now. We didn't get our blog post; we got a Response object. It has a status code (hopefully 200) and some headers, but the actual body of the message is still essentially a raw stream of bytes.
We need to tell JavaScript how to interpret that stream. Since we know this API sends JSON, we use the .json() method. Here is the kicker: .json() also returns a promise. So we have to chain another .then().
fetch('https://jsonplaceholder.typicode.com/posts/1')
.then(response => response.json())
.then(post => {
console.log('Here is the actual post:', post.title);
});
It works, but this "callback hell" style of chaining gets ugly fast when you have to do multiple things in a row. This is why I almost always use async and await. It makes asynchronous code look and behave like synchronous code.
Cleaning up the syntax
Let's rewrite that. To use await, we have to wrap the logic in a function marked as async. I like this approach because it reads like a story: "Fetch this, then wait for the JSON, then log it."
async function getPost() {
const response = await fetch('https://jsonplaceholder.typicode.com/posts/1');
const post = await response.json();
console.log('Much cleaner:', post.title);
}
getPost();
This is significantly easier to reason about. However, we're ignoring a huge reality of software engineering: the internet is flaky. Servers crash, DNS fails, and users lose their Wi-Fi. If the fetch call fails right now, the whole script will throw an uncaught error and crash.
When things go south
We need a safety net. In async/await land, that means a try...catch block. But there's a nuance here: fetch only "fails" (triggers the catch block) if there is a network error. If the server responds with a 404 (Not Found) or a 500 (Internal Server Error), fetch actually considers that a "success" because it technically received a response.
To handle this properly, we check the ok property of the response object first.
async function getPostSafely() {
try {
const response = await fetch('https://jsonplaceholder.typicode.com/posts/9999'); // ID that doesn't exist
if (!response.ok) {
throw new Error(`HTTP error! status: ${response.status}`);
}
const post = await response.json();
console.log(post);
} catch (error) {
console.error('Something went wrong:', error.message);
}
}
getPostSafely();
Now we're writing production-ready code. We've handled the promise, parsed the JSON, cleaned up the syntax, and accounted for the inevitable failure of the network. You're ready to start pulling real data into your UI.
📋 Practical Task
The Random Identity Generator
Your task is to build a small script that fetches a random user's profile from the randomuser.me API and displays it in the console.
Requirements:
- Create an
asyncfunction calledgenerateUser. - Fetch data from:
https://randomuser.me/api/ - Implement a
try...catchblock to handle any potential network errors. - Check if
response.okis true before parsing the JSON. - The API returns an object where the user data is inside an array called
results. You need to access the first element:data.results[0]. - Log a string to the console in this format:
"User Found: [FirstName] [LastName] from [City]".
There are no comments for now.