Within async functions, it's very common to "over-await" your code. For example, given the following functions:
function promptForDishChoice() {
return new Promise((resolve, reject) => {
const dialog = document.createElement("dialog");
dialog.innerHTML = `
<form method="dialog">
<p>What would you like to eat?</p>
<select>
<option value="pizza">Pizza</option>
<option value="pasta">Pasta</option>
<option value="salad">Salad</option>
</select>
<menu>
<li><button value="cancel">Cancel</button></li>
<li><button type="submit" value="ok">OK</button></li>
</menu>
</form>
`;
dialog.addEventListener("close", () => {
if (dialog.returnValue === "ok") {
resolve(dialog.querySelector("select").value);
} else {
reject(new Error("User cancelled dialog"));
}
});
document.body.appendChild(dialog);
dialog.showModal();
});
}
async function fetchPrices() {
const response = await fetch("/prices");
return await response.json();
}
You may write a function like this:
async function getPrice() {
const choice = await promptForDishChoice();
const prices = await fetchPrices();
return prices[choice];
}
However, note that the execution of promptForDishChoice
and fetchPrices
don't depend on the result of each other. While the user is choosing their dish, it's fine for the prices to be fetched in the background, but in the code above, the await
operator causes the async function to pause until the choice is made, and then again until the prices are fetched. We can use Promise.all
to run them concurrently, so that the user doesn't have to wait for the prices to be fetched before the result is given:
async function getPrice() {
const [choice, prices] = await Promise.all([
promptForDishChoice(),
fetchPrices(),
]);
return prices[choice];
}
Promise.all
is the best choice of concurrency method here, because error handling is intuitive — if any of the promises reject, the result is no longer available, so the whole await
expression throws.
Promise.all
accepts an iterable of promises, so if you are using it to run several async functions concurrently, you need to call the async functions and use the returned promises. Directly passing the functions to Promise.all
does not work, since they are not promises.
async function getPrice() {
const [choice, prices] = await Promise.all([
promptForDishChoice,
fetchPrices,
]);
}