Asynchronous JavaScript

Course 1 · Ch 10
Asynchronous JavaScript: Promises, async/await, and fetch
Dealing with things that take time — network requests, timers, and anything that doesn't finish instantly

Every example so far has run top to bottom, instantly. Real pages constantly wait on things that take time — loading data from a server being the most common. JavaScript handles this with promises, and the modern, readable way to work with them: async/await. This is the final Fundamentals chapter, and it pulls together functions (Ch 5), objects (Ch 7), and the DOM (Ch 8–9) into one realistic workflow.

The Problem: JavaScript Doesn't Wait

console.log("1. Starting"); setTimeout(() => { console.log("2. This runs later"); }, 1000); console.log("3. This runs before #2!"); // Logged order: 1, 3, 2 — NOT 1, 2, 3

setTimeout schedules a function to run later without pausing anything else. JavaScript moves straight on to the next line rather than waiting — this is what "asynchronous" means: code that will run eventually, not necessarily in the order it's written.

Promises — A Placeholder for a Future Value

const waitOneSecond = new Promise((resolve) => { setTimeout(() => { resolve("Done waiting!"); }, 1000); }); waitOneSecond.then((result) => { console.log(result); // "Done waiting!" — logged after ~1 second });

A Promise represents a value that doesn't exist yet, but will at some point — either successfully (resolve) or with an error (reject). .then() registers a function to run once the promise resolves, receiving the resolved value as its argument.

async/await — Promises Without the .then() Chains

async function run() { console.log("Before waiting"); const result = await waitOneSecond; console.log(result); // "Done waiting!" console.log("After waiting"); } run();

async before a function lets await be used inside it. await pauses that function (not the whole page) until the promise resolves, then continues with the resolved value — reading top-to-bottom almost exactly like the synchronous code from every earlier chapter, while still being genuinely non-blocking.

await only works inside an async function
Trying to use await in a normal function is a syntax error. This is the one new keyword pairing to remember: no async on the function, no await inside it.

fetch — Getting Real Data from a Server

async function getUser() { const response = await fetch("https://api.example.com/user/1"); const data = await response.json(); console.log(data.name); // the object's "name" property, from Chapter 7 } getUser();

fetch() requests a URL and returns a promise. The first await gets the response itself; response.json() then parses the body text into a real JavaScript object — another promise, needing its own await — at which point Chapter 7's dot/bracket notation works on it normally.

Handling Errors with try/catch

async function getUser() { try { const response = await fetch("https://api.example.com/user/1"); const data = await response.json(); console.log(data.name); } catch (error) { console.log("Something went wrong:", error.message); } }

A network request can fail — no connection, server error, invalid URL. Wrapping await calls in try/catch means a failure jumps straight to the catch block instead of leaving an unhandled error. Without this, a failed request can silently break the rest of the function with no clear feedback to the user.

A non-200 response doesn't automatically throw
fetch only rejects (triggering catch) on a genuine network failure — a 404 or 500 response is still considered a "successful" fetch as far as the promise is concerned. Check response.ok (a boolean) explicitly if a non-success status code needs separate handling.

Putting It Together: Fetch + DOM

async function loadUser() { const output = document.querySelector("#userOutput"); output.textContent = "Loading..."; try { const response = await fetch("https://api.example.com/user/1"); const user = await response.json(); output.textContent = `Hello, ${user.name}!`; } catch (error) { output.textContent = "Failed to load user."; } }

This is the complete, realistic pattern: show a loading state immediately, fetch data, then update the page with the result — or an error message if it fails. Every chapter since Chapter 5 contributes something here: functions, objects, DOM updates, and now asynchronous waiting.

Coding Challenges

Challenge 1

Write an async function delayedGreet(name) that uses await with a Promise wrapping setTimeout (1 second) to wait, then console.logs `Hello, ${name}!`. Call it and log "Calling..." immediately before, to confirm the greeting really does log after a delay.

📄 View solution
Challenge 2

Write an async function getPost(id) that fetches from `https://jsonplaceholder.typicode.com/posts/${id}`, parses the JSON, and logs the post's title. Wrap it in try/catch and log "Failed to fetch post." on any error.

📄 View solution
Challenge 3

Assume a button #loadBtn and a p#status. Add a click listener that's an async function: on click, set status text to "Loading...", await a fetch to `https://jsonplaceholder.typicode.com/users/1`, then set status to the user's email — or "Error loading data." if the fetch fails.

📄 View solution

Chapter 10 Quick Reference

  • Asynchronous code doesn't block — JavaScript moves on while it's pending
  • Promise — a placeholder for a value that resolves (success) or rejects (failure) later
  • async function — required wrapper to use await inside
  • await — pauses the async function (not the page) until a promise settles
  • fetch(url) — returns a promise for an HTTP response
  • response.json() — parses the response body into a usable object/array
  • try/catch — wraps await calls to handle network/parsing failures gracefully
  • response.ok — check explicitly for non-200 status codes; fetch won't throw on its own for those
  • This completes JavaScript Fundamentals. Intermediate begins with closures, the spread/rest operators, and ES6 classes.