Consuming A Promise With Then
The last post left a Promise sitting there, pending. This one actually does something with it once it settles.
The last post left a Promise sitting there, pending, right after fetch returned it. Time to actually do something once it settles.
Handling the Fulfilled Result with .then()
Every Promise has a .then() method built in, and it takes a callback to run once the Promise is fulfilled.
Hit Run. That callback receives one argument, the resolved value of the Promise, here called response. Log it and there's a status code, headers, a whole response object, but the actual data is tucked inside a body property that prints as an unreadable stream, not usable JSON yet.
The Response Body Needs Its Own Promise
Getting real data out of that body means calling response.json(), and that method is itself asynchronous. It returns a second Promise, not the parsed data directly.
Return that second Promise from the first .then() callback, and it can be handled with a second .then() right after.
Hit Run and the card renders, same result as the XMLHttpRequest version from a few posts back, reached through two chained .then() calls instead of one load event and a manual JSON.parse. The [data] in that last callback is the same array destructuring from a couple of posts ago, still unwrapping the one-result array a name search returns.
The Same Function, Simplified
Strip out the intermediate logging and swap in arrow functions, and the whole thing collapses further.
const getCountryData = (country) =>
fetch(`https://countries-api-836d.onrender.com/countries/name/${country}`)
.then((response) => response.json())
.then(([data]) => renderCountry(data));Read that line by line and it almost reads like English: fetch something, turn the response into JSON, render the data. Compare that to the nested load listeners from a few posts back, and the difference isn't just fewer characters, it's that this version is actually easy to trace from top to bottom.
Callbacks Didn't Disappear, the Nesting Did
Here's the part worth being precise about: those functions passed to .then() are still callbacks. Promises don't remove callbacks from JavaScript.
What they remove is the nesting. Each .then() sits at the same indentation level as the one before it, chained rather than tucked one level deeper inside the last one's callback.
ExpandChaining stays flat: each .then() sits at the same indentation level, while nested load listeners climb one level deeper for every dependent request.
That flat shape is what actually escapes the staircase from callback hell, and it matters more the moment a second dependent request (a neighboring country, say) gets added back in, which is exactly what's next.
The Essentials
.then()runs once a Promise is fulfilled, and its callback receives the resolved value.response.json()also returns a Promise, not the parsed data directly, so it needs its own.then().- Returning a Promise from inside a
.then()callback lets the next.then()chain onto it. - Chaining keeps every step at the same indentation level, instead of nesting one inside the last.
- Promises don't remove callbacks, they remove the nesting, which is the actual fix for callback hell.
Keep reading