JavaScript Async, Promises and Fetch
Some tasks take time, like waiting for a timer or loading data from a server. Asynchronous (async) code lets the page keep working while it waits.
Synchronous vs Asynchronous
Normal code runs line by line, and each line waits for the one before. If one line took 5 seconds, the whole page would freeze. Asynchronous code starts a slow task, carries on, and handles the result later when it is ready.
Think of ordering food at a restaurant. You do not stand at the kitchen door. You sit down, talk to friends, and the waiter brings the food when it is ready.
setTimeout and Callbacks
setTimeout(function, milliseconds) runs a function after a delay. The function you pass is called a callback.
Example
console.log("Order placed");
setTimeout(() => {
console.log("Food is ready");
}, 2000);
console.log("Waiting...");Order placed Waiting... Food is ready
"Waiting..." appears before "Food is ready", because the timer does not block the code below it. setInterval() works the same way but repeats.
Promises
A promise is an object that stands for a result that will arrive later. A promise is in one of three states:
| State | Meaning |
|---|---|
| Pending | Still working |
| Fulfilled | Finished with a result |
| Rejected | Failed with an error |
You create one with new Promise, and call resolve for success or reject for failure. Use .then() for the result and .catch() for errors.
Example
<!DOCTYPE html>
<html>
<body>
<p id="demo">Checking stock...</p>
<script>
function checkStock(item) {
return new Promise((resolve, reject) => {
setTimeout(() => {
const stock = { tea: 12, sugar: 0 };
if (stock[item] > 0) resolve(item + " is in stock (" + stock[item] + ")");
else reject(item + " is out of stock");
}, 1500);
});
}
checkStock("tea")
.then(message => document.getElementById("demo").innerHTML = message)
.catch(error => document.getElementById("demo").innerHTML = "Sorry: " + error);
</script>
</body>
</html>Try it Yourself »tea is in stock (12)
Change "tea" to "sugar" and run it again to see the catch part.
async and await
async and await make promise code look like normal step-by-step code. Put async before a function, then use await inside it to wait for a promise.
Example
<!DOCTYPE html>
<html>
<body>
<p id="demo"></p>
<script>
function wait(ms) {
return new Promise(resolve => setTimeout(resolve, ms));
}
async function deliver() {
const out = document.getElementById("demo");
out.innerHTML = "Order received...";
await wait(1000);
out.innerHTML += "<br>Rider picked it up...";
await wait(1000);
out.innerHTML += "<br>Delivered!";
}
deliver();
</script>
</body>
</html>Try it Yourself »Handling Errors With try...catch
With await, use try...catch to handle a rejected promise:
Example
async function load() {
try {
const result = await checkStock("sugar");
console.log(result);
} catch (error) {
console.log("Problem: " + error);
}
}Loading Data With fetch
fetch(url) asks a server for data and returns a promise. The response body is usually JSON, which you read with response.json() (another promise).
Example
async function loadUser() {
try {
const response = await fetch("https://jsonplaceholder.typicode.com/users/1");
if (!response.ok) {
throw new Error("Server error: " + response.status);
}
const user = await response.json();
document.getElementById("demo").textContent = user.name + " lives in " + user.address.city;
} catch (error) {
document.getElementById("demo").textContent = "Could not load data: " + error.message;
}
}
loadUser();The steps are always the same:
await fetch(url)to get the response- Check
response.ok(it is false for errors like 404 or 500) await response.json()to turn the body into a JavaScript object- Use the data to update the page
fetch only rejects when the network fails. A "404 Not Found" still counts as a response, so always check response.ok yourself.
Exercise
Exercise
Inside which kind of function can you use the await keyword?
