Throwing Errors And A Getjson Helper
The last post ended with a real gap: a 404 is still a fulfilled Promise, so .catch() never fires for it. Here's the actual fix.
The last post ended with a real gap: a 404 is still a fulfilled Promise, so .catch() never fires for it. Fixing that turns out to fix another gap too, the one from two posts back where a missing neighbor silently broke the chain.
response.ok Tells the Real Story
A response object carries an ok property, true for a status like 200, false for anything like 404. That's the piece .catch() has no way to see on its own.
fetch('https://countries-api-836d.onrender.com/countries/name/asdkjfhaskjdfh').then(
(response) => console.log(response.ok, response.status)
);Run that against a country name that doesn't exist and it logs false 404. The request still completed, technically, so the Promise is still fulfilled, response.ok is the only signal that anything actually went wrong.
Throwing an Error Rejects the Promise on Purpose
throw inside a .then() callback does something specific: it immediately rejects the Promise that callback returns, the same rejection a real network failure would cause, just triggered on purpose.
fetch('https://countries-api-836d.onrender.com/countries/name/asdkjfhaskjdfh')
.then((response) => {
if (!response.ok) throw new Error(`Country not found (${response.status})`);
return response.json();
})
.catch((error) => console.error(error.message)); ExpandA rejection propagates down through the chain until something catches it, whether that rejection came from a real network failure or a manual throw.
That thrown error travels down the chain exactly like any other rejection, all the way to whichever .catch() is waiting for it. There's nothing special about it once it's thrown, throw is just how a .then() callback creates a rejection on demand instead of waiting for one to happen naturally.
Fixing the Guard Clause From a Few Posts Back
That gap from a few posts back, if (!neighborCode) return;, quietly passed undefined down the chain instead of a Promise. The fix is the same technique: throw instead of returning silently.
Hit Run, then edit 'portugal' to 'australia', an island with no land borders. Instead of the old crash trying to read a flag off undefined, a real, readable message renders: this country has no land neighbors. Same fix, same technique, both places.
A Reusable getJSON Helper
That getJSON function above exists because the fetch-check-parse sequence, fetch, check response.ok, throw or call .json(), was about to get written out twice, once for the country and once for the neighbor. Wrapping it once and passing in the URL and a custom error message keeps that logic in exactly one place.
Every call site gets to skip straight to the data, the error checking already happened inside getJSON itself.
The Essentials
response.okisfalsefor an error status like 404, even though the Promise itself is still fulfilled.throwinside a.then()callback rejects that step's Promise on purpose, the same as a real network failure would.- A thrown error propagates down the chain to whichever
.catch()is waiting, no different from any other rejection. - A guard clause that silently returns should throw instead, so a missing value becomes a real, catchable error rather than a quiet
undefined. - Repeating the same fetch-check-parse steps at every call site is exactly what a small helper function like
getJSONexists to avoid.
Keep reading