I'm always perplexed when i see exceptions thrown in situations where i can't fathom crashing the system e.g https://developer.mozilla.org/en-US/docs/Web/API/Fetch_API/Using_Fetch
async function getData() {
const url = "";
try {
const response = await fetch(url);
if (!response.ok) {
throw new Error(`Response status: ${response.status}`);
}
const result = await response.json();
console.log(result);
} catch (error) {
console.error(error.message);
}
}
Why are systems built like this, what is the intuition on why a non 200 response should result in throwing an exception?Doesn't mean it crashes the system. Could be handled higher up
Their example code contains try / catch so the exception does not propagate.
Since fetch() can throw exceptions, you're going to need try / catch somewhere, so it can make sense for non-`ok` responses to also be handled as an exception.
I think the default behavior for most Clojure HTTP client libs is to throw on non-200 responses? I always pass the option to not throw, so I can handle non-200 responses myself. Just noting there's plenty of precedent for this.
I'm questioning why fetch throws in the first place. (I did misspeak about the throw in this code fwiw).
Why define a network timeout as a reason to throw, but not a status 500.
I don't need everything to be the same, but i would like some intuition on why this distinction exists.
For a network timeout/failure, you're not going to get a response back -- there's no status code etc -- so it's inherently not the same type of failure as a request that returns a non-200 status (and body). So their choice is to complicate the response object so you can tell whether the request completed or not, and what sort of failure it yields; or throw an exception for those cases, and keep the response object just for completed requests.
fetch() follows the principle of using exceptions for "unexpected" errors and assuming you'll handle them somewhere up your call tree (possibly at the top). And "expected" errors are in the response object as status codes etc.
I think a lot of languages that sit in-between functional error styles and more imperative ones tend to be inconsistent on which error is a thrown exception and which is a returned error.
Even if they try to follow some form of "criteria" like Sean mentioned
Thanks. @seancorfield i guess that is the intuition: it's not a response so it's an expectation. For my part, i feel like i would just wrap that in a try catch and turn it into data and then pass that on, not re-throw it.
whats confusing is the notion that responses would be handled a 'different place' then these exceptions.
Could, not would.
Ya, honestly I don't know. Maybe the author of this code tried to think what they consider "expected and unexpected" and separate thrown vs return based on that. But I could also see that, it was more accidental, the promise doesn't have a result and they were like, oh I should throw. Without much more thought.
In the example code, non-200 responses are turned into exceptions and all types of "failure" are then handled by catch. But you don't have to do that.
Oh nevermind, my JavaScript is too rusty. Thought response.ok was checking if the promise was in error or not.
So wait, then this code basically returns only a successful value or throws. Seems consistent then no?
If it didn't await directly on fetch(), it could handle the promise rejecting explicitly in the code (via callbacks). It's the await that throws when a promise rejects, right? (I don't generally do JS but that's what I assume).
Or I guess because of the catch around it all, it returns the success value or null ?
Lol no wait haha. This function returns nothing and never throws 😛. It just logs
I think Drew is more concerned with fetch() itself than the calling code...
Ah ok lol, but that function threw me off a cliff lol. Why it's called getData 😵💫
I think at this point we can all agree it's a poor example of supposed user code that invokes fetch() 🙂
I see, so @drewverlee your question is more about this line:
> The fetch() function will reject the promise on some errors, but not if the server responds with an error status like 404: so we also check the response status and throw if it is not OK.
I think then Sean is correct. Fetch fails the promise if the fetch failed (unable to receive a response). If the response indicates an "error", it doesn't consider that an error in the fetch, so it leaves that to be handled as a response for the code fetching.
It's the code example that creates confusion, "!response.ok" is not checking that the fetched promise is not in error. So why is the example than doing what it does for status 200 versus other statuses who knows, it's a poor example. (well it appears to do it because it wants to log the json response and I'd imagine that's only there if it's a 200