Exercise: Pizza Order Tracker

Write the order tracker for a busy pizza shop with async and await, Promise.all and Promise.any, from one order through the oven to the fastest free driver, and let the tests check each piece.

Journeyman · JavaScript · Updated

It's Friday night at Pie in the Sky Pizza. The kitchen's code is written: making the dough, adding the toppings and baking each take a few minutes, and each one returns a promise. Sometimes a promise is rejected, because the shop has run out of mushrooms or a driver is busy. What the shop doesn't have yet is a tracker: something that follows an order from dough to oven, cooks a whole table's pizzas at the same time, and finds the fastest free driver. That's three functions for you to write, and tests check every one as you go.

Get the exercise code

In the Exercise box beside the lesson, choose Get the starter on GitHub to open its GitHub repository. Click Code, then Download ZIP, and unzip it where you keep your practice folders. Or clone it with Git.

The folder holds:

  • tracker.js: the three functions, for you to finish.
  • kitchen.js: the kitchen. Its functions makeDough, addToppings, bake and askDriver each return a promise, and the comments say what each one fulfills with and when it rejects. Ready to use: read it, but don't change it.
  • track.js: runs a Friday-night rush with your functions and prints what happens. Ready to use.
  • tracker.test.js: the tests that check your functions. You don't need to change it.
  • package.json: tells Node.js how to run the code and its tests. Ready to use.

Everything runs in Node.js; there's no web page in this one. Open the folder in VS Code, and open a terminal there with Terminal > New Terminal.

What to do

Every function in tracker.js is already declared, with its parameters, and returns a placeholder for now. The comment above each one says what it should return, with examples:

// 1. trackOrder(order)
//    Follow one order through the kitchen: makeDough, then addToppings, then bake. Each step needs the pizza
//    the step before it gave back, so wait for each one before starting the next.
//    Write it as an async function, with await, and with try and catch.
//    When the pizza is baked, return 'Order <id>: <pizza> is ready'.
//    When a step fails, return 'Order <id>: sorry, <the error's message>'. Never let the error escape.
//    await trackOrder({ id: 101, pizza: 'Margherita', toppings: ['basil'] })
//      -> 'Order 101: Margherita is ready'
//    await trackOrder({ id: 102, pizza: 'Forest Mushroom', toppings: ['mushrooms', 'thyme'] })
//      -> 'Order 102: sorry, out of mushrooms'
export function trackOrder(order) {
  return '';
}
  1. Read kitchen.js. Find out what each kitchen function fulfills with, what it needs to be given, and when it rejects.
  2. Finish trackOrder. Make it an async function. Wait for each kitchen step with await, handing each one the pizza the step before it gave back, and catch a failure with try and catch, so the customer hears "sorry" instead of the program stopping.
  3. Finish orderForTheTable. A table orders several pizzas at once. Track every order at the same time with Promise.all, and return the lines in the same order as the orders.
  4. Finish fastestDelivery. Phone every driver at once with askDriver, and take the first one who says yes, with Promise.any. When nobody can take it, return 'No drivers free right now'.

Check your work

Run the tests from the terminal:

node --test

At first all 7 tests fail. That's expected: each function you finish turns its tests green. When a test fails, its message says what it expected, for example Return 'Order <id>: sorry, ' followed by the error's message. One test times the table's order: four pizzas cooked at the same time take about as long as one, and four cooked one after another take four times as long.

To watch your tracker run a Friday-night rush, run:

node track.js

Hints

  • Follow the pizza. makeDough fulfills with a pizza object, and addToppings needs that object, not the original order. Keep each step's result in its own variable with await, as in async and await.
  • One try for all three steps. A single try around the three await lines catches a failure from any of them. The catch gets the error, and its message is what the customer needs to hear.
  • Reuse what you've written. orderForTheTable can call your trackOrder once for each order. Calling it starts the order right away; Promise.all only does the waiting (see Waiting for Several Things at Once).
  • Why doesn't Promise.all give up here? Promise.all is all or nothing, but your trackOrder catches its own errors, so its promise is never rejected. A mushroom order comes back as a "sorry" line, and the rest of the table still gets their pizzas.
  • race or any? The busy driver answers first, to say no. Ask yourself which of the two waits for the first yes.

When you're done

All 7 tests are green, and node track.js prints:

One order, start to finish:
Order 101: Margherita is ready

A table for four:
Order 101: Margherita is ready
Order 102: sorry, out of mushrooms
Order 103: Pepperoni is ready
Order 104: Garden Veggie is ready

Who can take it?
Priya will deliver in 12 minutes
No drivers free right now

Now look back at your code. What would change if the shop decided that one failed pizza should cancel the whole table's order? Which of the four Promise tools would you use then?

So what

Waiting on slow work, one step after another, many at once, or the first that works, with a kind message when something fails: that's the heart of every app that talks to a server, from a food-delivery app's order screen to a game's online leaderboard. Next chapter, the slow work is real: data from the internet with fetch.

Key terms

Related