Promises were a huge improvement. But in 2017, JavaScript added something even nicer: async and await. They let you write asynchronous code that looks just like normal, top-to-bottom synchronous code.
Here's the same user-orders-items example, rewritten with async/await (using the same three functions):
async function showFirstOrder() {
const user = await getUser(1);
console.log(`1. Found user ${user.name}`);
const orders = await getOrders(user);
console.log(`2. Found ${orders.length} orders`);
const items = await getItems(orders[0]);
console.log(`3. First order contains: ${items.join(", ")}`);
}
showFirstOrder();
Output
1. Found user Olivia 2. Found 2 orders 3. First order contains: waffle, salsa
Isn't that beautiful? No .then(), no callbacks. It reads like a recipe.
Here are the rules:
- Put the keyword
asyncin front of a function to make it an async function. - Inside an async function, you can put
awaitin front of any promise.awaitpauses the function until the promise settles, and then gives you its value. - An async function always returns a promise.
await pauses the async function it's in, not your whole program. While that function is waiting, JavaScript happily carries on running other code, handling clicks and so on. It's like the waiter putting one order on hold, not the whole restaurant closing.
Handling errors with try...catch
With async/await, you handle errors using the good old try...catch from Summer School:
function orderPizza(flavor) {
return new Promise((resolve, reject) => {
setTimeout(() => {
if (flavor === "pineapple") reject(new Error("Out of pineapple. Phew."));
else resolve(`${flavor} pizza`);
}, 50);
});
}
async function dinner(flavor) {
try {
console.log(`Ordering ${flavor}...`);
const pizza = await orderPizza(flavor);
console.log(`😋 Eating a ${pizza}`);
} catch (error) {
console.log(`😢 No dinner: ${error.message}`);
} finally {
console.log("Washing up.");
}
}
async function main() {
await dinner("margherita");
await dinner("pineapple");
}
main();
It prints
Ordering margherita... 😋 Eating a margherita pizza Washing up. Ordering pineapple... 😢 No dinner: Out of pineapple. Phew. Washing up.
If the awaited promise is rejected, await throws the error, and the catch block catches it, exactly like a normal error.
If you forget await, you get the promise itself (the buzzer), not the value (the food):
async function getNumber() {
return 42;
}
async function main() {
const wrong = getNumber();
const right = await getNumber();
console.log(wrong);
console.log(right);
}
main();
It prints
Promise { <pending> }
42If you ever see Promise { <pending> } or [object Promise] where you expected real data, you probably forgot an await!