Skip to content
Retr0 Academy
JavaScript

JavaScript · 3 min read

Async JavaScript and Fetch

Understand asynchronous code, promises, and async/await, and load real data from APIs with fetch.

Some tasks take time: loading data from a server, waiting for a timer, reading a file. JavaScript does not freeze while it waits. Instead, it starts the task and continues, then handles the result when it is ready. This is asynchronous (async) code.

await pauses your function while the page stays responsive.
await pauses your function while the page stays responsive.

Timers: a first taste

js

console.log("1. Start");
 
setTimeout(() => {
  console.log("3. Runs after 1 second");
}, 1000);
 
console.log("2. End");

The output order is 1, 2, 3: the timer's callback runs later, even though it appears in the middle.

Promises

A promise is an object representing a value that will be available later. It can be:

  • pending: still working
  • fulfilled: finished successfully, with a value
  • rejected: failed, with an error

js

function wait(ms) {
  return new Promise((resolve) => setTimeout(resolve, ms));
}
 
wait(500)
  .then(() => console.log("Half a second later"))
  .catch((error) => console.error(error));

async and await

async/await lets you write async code that reads like normal, top-to-bottom code:

js

async function run() {
  console.log("Waiting...");
  await wait(1000);          // pause this function until the promise settles
  console.log("Done!");
}
run();
  • await can only be used inside an async function (or at the top level of a module).
  • An async function always returns a promise.

Handling errors

Use try...catch around await:

js

async function safeRun() {
  try {
    await somethingThatMightFail();
  } catch (error) {
    console.error("It failed:", error.message);
  } finally {
    console.log("Runs either way");
  }
}

Fetching data from an API

fetch requests data from a URL and returns a promise:

js

async function loadUser() {
  try {
    const response = await fetch("https://jsonplaceholder.typicode.com/users/1");
 
    if (!response.ok) {
      throw new Error("HTTP error " + response.status);
    }
 
    const user = await response.json(); // parse JSON body into an object
    console.log(user.name);
  } catch (error) {
    console.error("Could not load user:", error);
  }
}
 
loadUser();

Sending data

js

const response = await fetch("/api/progress", {
  method: "POST",
  headers: { "Content-Type": "application/json" },
  body: JSON.stringify({ lesson: "async", done: true }),
});

Try it: loading data with a spinner

This example loads a random quote from a public API. If the network is not available it falls back to a built-in list, so it always works.

<div class="box">
<p id="quote">Press the button to load a quote.</p>
<button id="load">Load quote</button>
</div>

Edit this example in the Playground.

Open in Playground

Running tasks in parallel

js

const [users, posts] = await Promise.all([
  fetch("/api/users").then((r) => r.json()),
  fetch("/api/posts").then((r) => r.json()),
]);

Promise.all waits for all promises together, which is faster than awaiting them one by one.

Summary

  • Async code starts a task and handles the result later without freezing the page.
  • Promises are pending, fulfilled, or rejected; handle them with .then / .catch.
  • async/await makes async code read top to bottom; wrap it in try...catch.
  • fetch loads data; check response.ok and parse with response.json().
Open in Playground

Saved in this browser.