jmtibs
jmtibs

Reputation: 255

Promise reject not triggering

I'm relatively new to Promises in js and am having trouble understanding why the following block of code does not run my catch function when the server responds with a 401 unauthorized.

loginUser(email, password).then((token) => {
  console.log("in then")
  //ipcRenderer.send('login-success', token)
}).catch(err => {
  console.log("in catch")   //not running
})

The loginUser function:

function loginUser(email, password) {
  let body = { email: email, password: password  }

  return fetch('http://localhost:3000/api/v1/sessions', {
    method: 'POST',
    body: JSON.stringify(body),
    headers: { 'Content-Type': 'application/json' }
    }).then(response => {
       return response.json().then(json => {
         console.log(response.ok) // false
         return response.ok ? json : Promise.reject(json)
    }).catch(err => {
      console.error(err)
    })
  })
}

Any help at all would be appreciated. Cheers

Upvotes: 3

Views: 3734

Answers (3)

jfriend00
jfriend00

Reputation: 707318

I see a couple problems here:

First, a 401 response coming back from a fetch() does not reject. That's a SUCCESSFUL http request. It contacted the server, sent the request and got the response. The fact that you got a 401 status back is up to your application how to handle. A 401 status does not reject.

From the MDN doc for fetch():

The Promise returned from fetch() won’t reject on HTTP error status even if the response is an HTTP 404 or 500. Instead, it will resolve normally (with ok status set to false), and it will only reject on network failure or if anything prevented the request from completing.

Second, when you do this:

}).catch(err => {
  console.error(err)
})

You are catching a rejected promise, handling it and turning it into a resolved promise (just like a try/catch stops a thrown exception). So your function, as written, could never return a rejected promise). If you want to log like that, but preserve the rejected promise, then you need to rethrow the error:

}).catch(err => {
  console.error(err)
  throw err;
})

If you wanted a resolved promise only when you get valid data, you could specifically make the promise reject with other statuses or you could check for the response.ok from fetch and turn that into a rejection:

function loginUser(email, password) {
  let body = { email: email, password: password  }

  return fetch('http://localhost:3000/api/v1/sessions', {
    method: 'POST',
    body: JSON.stringify(body),
    headers: { 'Content-Type': 'application/json' }
    }).then(response => {
       // make sure that we actually got data
       if (!response.ok) {
          throw new Error(`No response.ok.  Got http status ${response.status}`);
       }
       return response.json().then(json => {
         console.log(response.ok) // false
         return response.ok ? json : Promise.reject(json)
    }).catch(err => {
      console.error(err);
      throw err;
    });
  })
}

Third, since the error you refer to is an authorization error, you should be warned that fetch() does not, by default, send any cookies so if you were relying on cookies for authentication, you will have to configure your fetch() request specifically to send cookies using the fetch option: credentials: 'include'.

Upvotes: 0

marzelin
marzelin

Reputation: 11600

Promise.reject()
  .catch(() => console.log("rejection is caught in first `catch`"))
  .then(() => console.log("`catch` returns fulfilled  promise so `then` is executed"))
  .catch(() => console.log("this won't be executed"))

Upvotes: 3

timothyclifford
timothyclifford

Reputation: 6959

From fetch GitHub:

https://github.com/github/fetch/issues/201

Fetch API fails only if it can't make a request. And if it can, fetch will be executed successfully even if it has a bad status.

So it sounds like your .then( branch will be handling the 401 and you will need to handle it here.

.catch( will only execute if the request can't be made.

Upvotes: 4

Related Questions