BACK

Mar 9, 2025

0 words
0 m

The First Time I Understood Async

I remember the first time I saw asynchronous code.
It felt confusing.

I thought code always ran top to bottom. Line by line. Like reading a page in a book.
But then I saw this:

console.log("One");

setTimeout(() => {
  console.log("Two");
}, 1000);

console.log("Three");

And the output was:


That didn’t make sense at first. Why would “Three” appear before “Two”?

The simple idea

The trick is this: when something takes time, JavaScript doesn’t stop.
It keeps going, and comes back later.

The timer is like setting an alarm. The program keeps working, and when the alarm rings, it finishes that task.

Callbacks

At first I used callbacks:

function getData(callback) {
  setTimeout(() => {
    callback("Here is your data");
  }, 1000);
}

getData((result) => {
  console.log(result);
});

This worked, but when I had to wait for many things, the code got messy:

getData((a) => {
  getData((b) => {
    getData((c) => {
      console.log(a, b, c);
    });
  });
});

Hard to read. Hard to follow.

Promises

Promises made it a little cleaner. They are just a way of saying: I’ll give you the result later.

function getData() {
  return new Promise((resolve) => {
    setTimeout(() => {
      resolve("Here is your data");
    }, 1000);
  });
}

getData().then((result) => {
  console.log(result);
});

Better, but chaining still felt heavy:

getData()
  .then((a) => getData().then((b) => [a, b]))
  .then(([a, b]) => console.log(a, b));

Async/await

Then I learned async and await. Suddenly it looked like normal code again.

async function main() {
  const a = await getData();
  const b = await getData();
  console.log(a, b);
}

main();

Clear steps: wait, then move on. Easy to read. Easy to write.
That was the moment it clicked.

Why it matters

Async is about not blocking. While one thing takes time, the rest of the program can keep moving.
It’s like putting food in the oven and doing the dishes while you wait.

Without async, you would just stand there, staring at the oven until the timer goes off.

Real examples

Fetching from an API

async function fetchUser(id) {
  const res = await fetch(`/api/users/${id}`);
  return res.json();
}

async function showUser() {
  try {
    const user = await fetchUser(42);
    console.log(user.name);
  } catch (e) {
    console.error("Error:", e.message);
  }
}

showUser();

Reading a file

import { promises as fs } from 'fs';

async function readFile() {
  const data = await fs.readFile('notes.txt', 'utf8');
  console.log(data);
}

readFile();

Running tasks together

async function main() {
  const [a, b] = await Promise.all([getData(), getData()]);
  console.log(a, b);
}

main();

The rule I follow

  • Use await when one step depends on the last.

  • Use Promise.all when steps can run at the same time.

Final thought

The day I understood async, programming felt different.
It wasn’t magic anymore. It was just about letting the program keep going while waiting for something slow.

That small shift made everything clearer - and a lot less scary.

Taseen Tanvir

1:12:28 UTC

Create a free website with Framer, the website builder loved by startups, designers and agencies.