await means wait here until the buzzer goes off, then keep going. Your code reads like a normal story, top to bottom, with no .then needed. Underneath, it is still the same promises.
async on the function, await on the promise
Put async before the function. Then put await before any promise inside it. The function pauses at that line until the promise is fulfilled, and await hands you the value.
function fetchPizza() {
return new Promise(resolve => setTimeout(() => resolve("pizza"), 300));
}
async function dinner() {
const pizza = await fetchPizza();
console.log("Eating " + pizza); // prints: Eating pizza
}
dinner();
console.log("Doing homework"); // prints: Doing homework
"Doing homework" prints first. Only dinner waits. The rest of your program keeps running, and dinner picks up where it left off once the pizza arrives.
An async function always returns a promise, so other code can await it or use .then on it.
Compare it with the same job written using .then.
const fetchPizza = () => Promise.resolve("pizza");
fetchPizza().then(pizza => console.log("Eating " + pizza)); // prints: Eating pizza
For one step, both are short. With three or four steps, the async version stays a plain list of lines, and each result gets a normal variable name you can use further down. That is the main reason people prefer it. Nothing new happens underneath. await is a nicer way to write the same .then.
When it fails: try and catch
If the promise is rejected, await throws the error. Put the risky part in try, and JavaScript jumps straight into catch when something goes wrong.
function fetchPizza() {
return Promise.reject(new Error("kitchen fire"));
}
async function dinner() {
try {
const pizza = await fetchPizza();
return "Eating " + pizza;
} catch (err) {
return "No dinner: " + err.message;
}
}
dinner().then(msg => console.log(msg)); // prints: No dinner: kitchen fire
This is the same try and catch you use for normal errors, so you don't need a separate style for slow code.
The twist: Promise.all for speed
Starting one thing, waiting for it, and only then starting the next is slow. If two jobs don't depend on each other, start both right away and let Promise.all wait for them together.
const wait = (ms, food) => new Promise(r => setTimeout(() => r(food), ms));
async function order() {
const [a, b] = await Promise.all([wait(200, "pizza"), wait(200, "juice")]);
return [a, b];
}
order().then(meal => console.log(meal)); // prints: [ 'pizza', 'juice' ]
Both jobs run at the same time, so this takes about 200 milliseconds instead of 400. The results come back in the same order you listed the promises. If any one of them is rejected, Promise.all is rejected too.
One more thing. await only works inside an async function, or at the very top level of a module, which is a special kind of JavaScript file.
When to use it
Use async and await for most new code that deals with promises, especially when there are several steps in a row. It reads like a recipe, and errors use the familiar try and catch.
.then is still fine for a quick one-step job. Both styles work with the same promises, so you can mix them, like calling .then on the result of an async function.
A good habit is to look at each await and ask whether it really needs the line above it to finish first. If it doesn't, start the jobs together and use Promise.all. Waiting in a row when you don't have to is the most common way async code ends up slower than it should be.
Rule of thumb
Put async on the function and await on the promise.
Forgetting await. Without it, const pizza = fetchPizza(); gives you the promise itself, the buzzer instead of the food, which logs as something like Promise { <pending> }.
Check yourself
const pizza = await fetchPizza(); do?await pauses only the async function it is in and gives you the fulfilled value. The rest of the program keeps running.