Then, Catch and Finally
You know that .then() runs when a promise succeeds. But what happens when something goes wrong? And what if you need something to run no matter what? That is what .catch() and .finally() are for.
.then() - when it works
.then() receives the fulfilled value and runs your code with it:
fetch("/patients")
.then(response => response.json())
.then(patients => {
console.log("Got", patients.length, "patients")
})
If everything goes right, .then() runs. If anything fails, .then() is skipped.
.catch() - when something goes wrong
Without .catch(), errors are silent. The page just does nothing and you have no idea why.
.catch() receives the error and lets you handle it:
fetch("/patients")
.then(response => response.json())
.then(patients => console.log(patients))
.catch(error => {
console.log("Request failed:", error)
alert("Could not load patients. Please try again.")
})
Common reasons .catch() runs:
- No internet connection
- The server is not running
- The server returned an error and the JSON could not be parsed
.catch() at the end catches errors from any of the .then() calls above it.
.finally() - runs always
.finally() runs whether the promise succeeded or failed. It is useful for cleanup - like hiding a loading spinner.
// show a spinner before the request
document.getElementById("spinner").style.display = "block"
fetch("/patients")
.then(response => response.json())
.then(patients => console.log(patients))
.catch(error => console.log("Error:", error))
.finally(() => {
// always hide the spinner when done
document.getElementById("spinner").style.display = "none"
})
The spinner disappears whether the request worked or not.
Side by side - with and without error handling
Without:
fetch("/patients")
.then(response => response.json())
.then(patients => buildTable(patients))
If this fails, you see nothing. No error, no message. The table just stays empty.
With:
fetch("/patients")
.then(response => response.json())
.then(patients => buildTable(patients))
.catch(error => {
document.getElementById("error-msg").textContent = "Failed to load. Check your connection."
})
.finally(() => {
document.getElementById("spinner").style.display = "none"
})
Now failures are visible to the user and you can act on them.
Challenge
Take this fetch call and add .catch() and .finally() to it:
fetch("https://jsonplaceholder.typicode.com/users")
.then(response => response.json())
.then(users => console.log("Users:", users.length))
.catch()should log the error message to the console.finally()should log "Done" no matter what
Then test what happens when you change the URL to something that does not exist.