A Chef’s guide to asynchronous javaScript

When I first started writing JavaScript, I thought of code as an assembly line: one step happens, then the next, until the final product appears. I was wrong. Real-world cooking taught me better. In a professional kitchen, multiple tasks — chopping, sautéing, baking — happen in parallel, and timing matters. JavaScript’s asynchronous model works much the same way, letting you “prep” one thing while another cooks.

The synchronous chef: A recipe for disaster

Imagine a chef working alone in a kitchen, preparing a big meal with turkey, potatoes, bread, and green beans. If they followed a synchronous approach, they'd do everything one after the other:

  1. Prepare the turkey (30 minutes)

  2. Prepare the potatoes (20 minutes)

  3. Bake the bread (15 minutes)

  4. Cook the green beans (10 minutes)

By the time everything is done, the turkey is cold, the bread is stale, and the potatoes are lukewarm. Not ideal.

This is exactly what happens in synchronous JavaScript. Each operation blocks the next until it finishes. For example:

function prepareTurkey() {
  console.log("Preparing turkey...");
  // simulate delay
  for (let i = 0; i < 1e9; i++) {} 
  console.log("Turkey is ready!");
}

function preparePotatoes() {
  console.log("Preparing potatoes...");
  for (let i = 0; i < 5e8; i++) {} 
  console.log("Potatoes are ready!");
}

function bakeBread() {
  console.log("Baking bread...");
  for (let i = 0; i < 3e8; i++) {} 
  console.log("Bread is ready!");
}

function cookGreenBeans() {
  console.log("Cooking green beans...");
  for (let i = 0; i < 2e8; i++) {} 
  console.log("Green beans are ready!");
}

prepareTurkey();
preparePotatoes();
bakeBread();
cookGreenBeans();

The asynchronous chef: handling tasks like a pro

A skilled chef doesn’t work this way. Instead, they multitask efficiently:

  • Put the turkey in the oven (starts cooking, doesn’t need constant attention).

  • While the turkey roasts, peel the potatoes (another task begins).

  • While potatoes boil, knead the bread dough.

  • While bread rises, sauté the green beans.

This is asynchronous execution. Tasks overlap, and the chef switches between them as needed.

In JavaScript, we achieve this using:

  • Callbacks (older approach, can lead to "callback hell")

  • Promises (cleaner chaining with .then() and .catch())

  • Async/Await (modern, readable syntax)

Here’s how we’d simulate the chef’s workflow with setTimeout to simulate parallel prep in a non-blocking way — just like a well-organized kitchen where multiple things can cook at the same time:

function prepareTurkey() {
  console.log("Started preparing turkey...");
  setTimeout(() => {
    console.log("Turkey is ready!");
  }, 3000); // 3 seconds
}

function preparePotatoes() {
  console.log("Started preparing potatoes...");
  setTimeout(() => {
    console.log("Potatoes are ready!");
  }, 2000); // 2 seconds
}

function bakeBread() {
  console.log("Started baking bread...");
  setTimeout(() => {
    console.log("Bread is ready!");
  }, 1500); // 1.5 seconds
}

function cookGreenBeans() {
  console.log("Started cooking green beans...");
  setTimeout(() => {
    console.log("Green beans are ready!");
  }, 1000); // 1 second
}

prepareTurkey();
preparePotatoes();
bakeBread();
cookGreenBeans();

or using promises (like scheduling tasks with timers and helpers):

function prepareTurkey() {
  return new Promise((resolve) => {
    console.log("Started preparing turkey...");
    setTimeout(() => {
      resolve("Turkey is ready!");
    }, 3000);
  });
}

function preparePotatoes() {
  return new Promise((resolve) => {
    console.log("Started preparing potatoes...");
    setTimeout(() => {
      resolve("Potatoes are ready!");
    }, 2000);
  });
}

function bakeBread() {
  return new Promise((resolve) => {
    console.log("Started baking bread...");
    setTimeout(() => {
      resolve("Bread is ready!");
    }, 1500);
  });
}

function cookGreenBeans() {
  return new Promise((resolve) => {
    console.log("Started cooking green beans...");
    setTimeout(() => {
      resolve("Green beans are ready!");
    }, 1000);
  });
}

// Run all at once
Promise.all([
  prepareTurkey(),
  preparePotatoes(),
  bakeBread(),
  cookGreenBeans()
]).then((results) => {
  results.forEach((message) => console.log(message));
  console.log("Dinner is served!");
});

or a cleaner version using async/await (like a head chef delegating tasks but still coordinating them):

async function prepareDinner() {
  console.log("Dinner prep started...");

  const turkeyPromise = prepareTurkey();
  const potatoesPromise = preparePotatoes();
  const breadPromise = bakeBread();
  const beansPromise = cookGreenBeans();

  const turkey = await turkeyPromise;
  const potatoes = await potatoesPromise;
  const bread = await breadPromise;
  const beans = await beansPromise;

  console.log(turkey);
  console.log(potatoes);
  console.log(bread);
  console.log(beans);
  console.log("Dinner is served!");
}

prepareDinner();

Why asynchronous code matters

Just like our chef, JavaScript runs in a single-threaded environment (one chef, one kitchen). If we block operations, the whole page freezes—imagine a website that can’t respond to clicks while loading data!

Instead, we use:

  • Event loop: JavaScript’s way of managing task switching (like the chef checking the oven, then the stove).

  • Web APIs: Browser features (setTimeout, fetch) that handle tasks outside the main thread.

  • Non-blocking code: Operations that start now, finish later, without freezing everything else.

Final thoughts: cooking smarter, not harder

Trying to do everything one step at a time is inefficient — in both cooking and code. That’s where asynchronous JavaScript shines.

By learning to use callbacks, promises, and especially async/await, you can write programs that stay fast, responsive, and organized — just like a well-run kitchen.

So next time you're juggling API calls or working with timeouts, remember: you're not just writing code — you're running the kitchen. And JavaScript gives you everything you need to cook like a pro.

Bon appétit, and happy coding!

0
Subscribe to my newsletter

Read articles from Leonardo Huguenin Pereira directly inside your inbox. Subscribe to the newsletter, and don't miss out.

Written by

Leonardo Huguenin Pereira
Leonardo Huguenin Pereira