JavaScript Tutorial 7: Asynchronous JS & APIs

Stepping into the Real World

Up until now, we have hardcoded all our data directly into our JavaScript files (like typing out an array of products). However, in the real world, if you are building the frontend for a platform like the Animal Market, the actual data lives on a backend server (likely controlled by Python/Django and a database like MongoDB). Today, we learn how to make JavaScript reach across the internet to fetch that data dynamically.

Step 1: Synchronous vs. Asynchronous Code

By default, JavaScript is Synchronous. It reads your code line by line, from top to bottom. It will not move to Line 2 until Line 1 is completely finished. This is called "blocking" code.

The Problem: Imagine requesting a massive list of inventory from your database. If the server takes 3 seconds to reply, a Synchronous website will completely freeze for 3 seconds. The user cannot click buttons, scroll, or do anything. The website looks broken.

The Solution: Asynchronous Code. This tells the browser: "Hey, I am going to request this data. It might take a while. Send this task to the background, keep running the rest of the website, and let me know when the data finally arrives."

Step 2: JavaScript Promises (The "I Owe You")

When you ask a server for data, it doesn't give you the data immediately. It gives you a Promise. A Promise is exactly what it sounds like: a guarantee that the browser will eventually return with a result. A Promise has three states:

  1. Pending: The request was sent. The browser is waiting.
  2. Fulfilled: The server replied successfully with the data!
  3. Rejected: The server crashed, or the user's internet went down.

Step 3: The Fetch API (Getting the Data)

To ask a server for data, we use the built-in fetch() function. You pass it a URL (an API Endpoint), and it returns a Promise.

The traditional way to handle a Promise is using the .then() method. It literally means: "Fetch this data, and then do this, and then do that."

The JavaScript: The .then() Chain

// 1. We ask the server for data
fetch("https://api.animalmarket.com/v1/inventory")
  // 2. The server responds with raw, unreadable HTTP packets. We must convert it to JSON.
  .then( (response) => {
    return response.json();
  })
  // 3. NOW we have our actual JavaScript Array of Objects!
  .then( (data) => {
    console.log("Data received:", data);
  })
  // 4. Catch any errors (like a 404 Not Found)
  .catch( (error) => {
    console.log("Something went wrong!", error);
  });

Step 4: Async / Await (The Modern Standard)

While .then() chaining works, it can get very messy and hard to read. In 2017, JavaScript introduced Async / Await. It allows you to write Asynchronous code that looks like normal Synchronous code, making it incredibly clean.

We pair this with a try / catch block (which functions exactly like the try / except blocks you might have used when handling files in Python).

The Rule of Await: You can ONLY use the await keyword inside a function that has been labeled with the async keyword. await tells the code: "Pause this specific function right here until the Promise finishes."

The HTML, CSS, & JavaScript: Building a Live Data Feed

<!-- HTML -->
<div class="inventory-dashboard">
  <button id="load-btn">Fetch Live Inventory</button>
  <div id="feed"></div>
</div>

/* CSS (Written in stylesheet) */
.card { border: 1px solid #ccc; padding: 15px; border-radius: 8px; }

/* JavaScript */
const feedArea = document.querySelector("#feed");
const loadBtn = document.querySelector("#load-btn");

// 1. Mark the function as 'async'
async function getInventoryData() {
  try {
    // Show a loading state
    feedArea.innerHTML = "<p>Contacting Database...</p>";

    // 2. Await the fetch (Code pauses here until data arrives)
    // (Using a dummy API that returns random users for this example)
    const response = await fetch("https://jsonplaceholder.typicode.com/users");

    // 3. Await the JSON conversion
    const data = await response.json();

    // 4. Clear the loading text
    feedArea.innerHTML = "";

    // 5. Loop through the fetched data and inject HTML
    data.slice(0, 3).forEach( (user) => {
      const cardHTML = "<div class='card'><h3>" + user.name + "</h3><p>Email: " + user.email + "</p></div>";
      feedArea.innerHTML += cardHTML;
    });

  } catch (error) {
    // If the internet dies or the URL is wrong, it safely catches it here
    feedArea.innerHTML = "<p style='color:red;'>Failed to load data.</p>";
  }
}

// Connect the button
loadBtn.addEventListener("click", getInventoryData);

The Simulated Output (Click the button below!):

Feed is currently empty. Waiting for fetch...

Final Quiz: Test Your Asynchronous Logic

Click the buttons below to verify your knowledge.

1. What does it mean when we say a JavaScript `fetch()` request is Asynchronous?
2. If you want to use the `await` keyword to pause code execution until a database replies, what MUST you do to the function wrapping it?
3. Why do we wrap our `await fetch()` calls inside a `try / catch` block?

Post a Comment

0 Comments