When you bring in web services, your application gains new ways to fail. You need to be able to detect these errors and either recover from them or communicate them to the viewer. Exceptions are a common mechanism for handling errors in synchronous code. You might think you can handle an error with fetch
using a try-catch block:
You are wrong. Try-catch is used to handle exceptions that occur synchronously in the try block. The fetch
function runs asynchonously. To handle asynchronous exceptions, you use the catch
function provided by the promise that fetch
returns:
The callback provided to catch
here just echoes the unhelpful error message in the pre
element. A website in production must provide a more readable message and give the viewer a way forward.
The above error is caused by a URL that doesn't point to a real domain. What happens if the URL points to a bad endpoint on a valid domain?
Do you see an error message? Try popping open this page and inspecting the console.
You see an error message, but it doesn't bubble up into your catch-handler. That's funny. You add a then-handler to your promise chain to inspect this issue further:
Pop open this page in its window and inspect the response in the console. What do you notice?
Technically, the fetch
itself did not fail. The request arrived at the service just fine, and the client received a response just fine. The only issue was that the client sent an unsatisfiable request, which the service indicated in its response by setting the status code to 404.
To handle failed requests—as opposed to failed fetches—you could add a conditional statement inside the then-handler:
However, it'd be slick if you could make failed requests behave like failed fetches. Then all your error-handling could be consolidated in your callback to catch
. As you think about the problem, you think back to the chaining mechanics of promises, and you come up with this assertResponse
function:
Note how assertResponse
has been inserted as the first then-handler in the promise's chain. The function is only named, not called. You are passing it as a callback.
The service sends a status code in the 200s if the request was satisfied. If the response has a status code in this range, your function returns it so it can be passed along to the next stage in the promise's chain. Otherwise, an Error
object is thrown. The second then-handler is skipped over and the catch-handler runs.
The error handling code is consolidated, and the then-handler is simplified since it can assume that it's receiving a successful response. You decide to insert assertResponse
as the first stage of all your fetch
calls for the rest of your career as a web developer.