Mini Project: The Burger Kitchen Simulator

Freshman Year, page 9 of 10. A two-minute read.

Let's simulate a busy restaurant kitchen. Some steps must happen in order, some can happen at the same time, and sometimes things go wrong. We'll use short delays so it runs quickly, and print a timestamp for each step so you can see what happens when.

kitchen.js
const start = Date.now();
const log = (message) => {
  const ms = String(Date.now() - start).padStart(4, " ");
  console.log(`[${Math.round(ms / 100) * 100}ms] ${message}`);
};

const wait = (ms) => new Promise((resolve) => setTimeout(resolve, ms));

async function prepare(task, ms) {
  log(`⏳ Starting: ${task}`);
  await wait(ms);
  log(`✅ Done: ${task}`);
  return task;
}

async function checkPantry(ingredient) {
  await wait(100);
  if (ingredient === "saffron") {
    throw new Error(`We're out of ${ingredient}!`);
  }
  return ingredient;
}

async function cookJollof() {
  log("👩‍🍳 Order received: Fried rice with chicken");

  // These are independent, so do them at the same time
  await Promise.all([
    prepare("wash the rice", 200),
    prepare("blend tomatoes and peppers", 300),
    prepare("season the chicken", 300),
  ]);

  // These must happen in order
  await prepare("fry the tomato stew", 400);
  await prepare("add rice and simmer", 500);

  log("🍛 Burger is served! Enjoy!");
}

async function fancyDish() {
  try {
    await checkPantry("saffron");
    log("This will never print");
  } catch (error) {
    log(`🚫 Fancy dish cancelled: ${error.message}`);
  }
}

async function main() {
  await cookJollof();
  await fancyDish();
  log("🧽 Kitchen closed.");
}

main();

It prints

[0ms] 👩‍🍳 Order received: Fried rice with chicken
[0ms] ⏳ Starting: wash the rice
[0ms] ⏳ Starting: blend tomatoes and peppers
[0ms] ⏳ Starting: season the chicken
[200ms] ✅ Done: wash the rice
[300ms] ✅ Done: blend tomatoes and peppers
[300ms] ✅ Done: season the chicken
[300ms] ⏳ Starting: fry the tomato stew
[700ms] ✅ Done: fry the tomato stew
[700ms] ⏳ Starting: add rice and simmer
[1200ms] ✅ Done: add rice and simmer
[1200ms] 🍛 Burger is served! Enjoy!
[1300ms] 🚫 Fancy dish cancelled: We're out of saffron!
[1300ms] 🧽 Kitchen closed.

(We round the timestamps to the nearest 100 ms, because real timers are never perfectly on time.)

Look at the timeline. The three prep tasks all start together at 0 ms, because of Promise.all. The stew can't start until all the prep is done (at 300 ms, the time of the slowest prep task). The simmering waits for the stew. The total cooking time is 1.2 seconds. If we'd done the prep tasks one after another, it would have taken 1.7 seconds. And when the pantry check fails, try...catch handles it gracefully, and the kitchen still closes properly.

Try it yourself
  1. Add a fourth prep task ("chop onions", 250 ms) to the Promise.all. Does the total time change? Why or why not?
  2. What happens to the total time if the "season the chicken" task takes 600 ms instead?
  3. Change checkPantry("saffron") to checkPantry("salt"). What prints now?

Freshman year: complete! Asynchronous code is genuinely one of the trickiest parts of JavaScript, and you've just conquered it. Every app that loads data, every chat app, every online game: they all use exactly what you just learned.

In your Sophomore year, we'll learn how to organize bigger programs using classes and object-oriented programming. Plus, we'll finally solve the mystery of this.

Reading is step one

CurlyScript turns these notes into a game: short questions, an explanation after every answer, and a test at the end of each grade. Free, with nothing to sign up for.

Start learning