Why is `.then()` processed when fetch fails?

610 views Asked by At

Consider the following code where I tried to shield fetch against any unsuccessful connections (I call them non "200-ish" in the comments) and provide a function that will make use of its successful results:

const callApi = () => {
  return fetch("http://doesnotexist.example.com")
    .then((r) => {
      // check for non200-ish respnses (404, etc.)
      if (!r.ok) {
        console.log(`status for failed call: ${r.status}`);
        throw new Error(`${r.statusText} (${r.status})`);
      } else {
        // continue the chain because the result is 200-ish
        return r;
      }
    })
    .then((r) => r.json())
    .catch((err) => {
      // should catch network errors (DNS, etc.) as well as replies that are not 200-ish
      console.log(`call failed: ${err}`);
    });
};

callApi().then((r) => console.log("the call was successful"));

The result is

call failed: TypeError: Failed to fetch
the call was successful

Since this is a network issue, the first then() was not executed and we jumped directly to the catch(). But why has the last then() been executed?

The next example is for a call that returns an error code:

const callApi = () => {
  return fetch("https://httpstat.us/500")
    .then((r) => {
      // check for non200-ish respnses (404, etc.)
      if (!r.ok) {
        console.log(`status for failed call: ${r.status}`);
        throw new Error(`${r.statusText} (${r.status})`);
      } else {
        // continue the chain because the result is 200-ish
        return r;
      }
    })
    .then((r) => r.json())
    .catch((err) => {
      // should catch network errors (DNS, etc.) as well as replies that are not 200-ish
      console.log(`call failed: ${err}`);
    });
};

callApi().then((r) => console.log("the call was successful"));

The output is

status for failed call: 500
call failed: Error: Internal Server Error (500)
the call was successful

Same question as above.

Finally, for 200 everything is fine:

const callApi = () => {
  return fetch("https://httpstat.us/200")
    .then((r) => {
      // check for non200-ish respnses (404, etc.)
      if (!r.ok) {
        console.log(`status for failed call: ${r.status}`);
        throw new Error(`${r.statusText} (${r.status})`);
      } else {
        // continue the chain because the result is 200-ish
        return r;
      }
    })
    .catch((err) => {
      // should catch network errors (DNS, etc.) as well as replies that are not 200-ish
      console.log(`call failed: ${err}`);
    });
};

callApi().then((r) => console.log("the call was successful"));

Another way to address the question would be: how to stop processing at the catch()?

1

There are 1 answers

7
Cerbrus On

You're returning the result of a fetch().then().catch() chain, and calling a .then() on that:

callApi().then((r) => console.log("the call was successful"));

That last .then() will always be executed, because the promise was handled successfully. It either:

  • Completed successfully, or
  • catch took care of any errors that occurred`