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
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
Share Improve this question asked Jul 23, 2017 at 22:23 jmtibsjmtibs 2552 gold badges5 silver badges11 bronze badges 5-
Does any of your code run? Are you sure it's not executing your
then
clause? What do you see in your console and network tab? – timothyclifford Commented Jul 23, 2017 at 22:27 - @timothyclifford sry should have provided more details from the get-go. Code does execute. I see three things in my console when intentionally entering in a bad email/pass bo. I see the server response from fetch, 401 unauthorized, I see the actual error object from the console.error(err) in the catch of the fetch call, and I see the console.log output "in then", which I expect to be "in catch" – jmtibs Commented Jul 23, 2017 at 22:32
- you are catching the error in login user. After catching, the promise is considered 'fixed' if it doesn't throw. – marzelin Commented Jul 23, 2017 at 22:32
- @marzelin ah I see. That makes sense now. Thank you. – jmtibs Commented Jul 23, 2017 at 22:33
-
.catch(err => {console.error(err)}
makes it so you will NEVER return a rejected promise because this takes a rejected promise and turns it into a resolved promise. If you want to log like this, then you have addthrow err
to the.catch()
handler to rethrow the error and keep the promise rejected. Also, a 401 status is still a successful http request. The server was contacted and a response returns. That does not reject. So, you need to either check the status in your.then()
handler or specifically change a non-2xxx response into a rejection yourself. – jfriend00 Commented Jul 24, 2017 at 0:00
3 Answers
Reset to default 4From fetch
GitHub:
https://github./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.
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"))
I see a couple problems here:
First, a 401 response ing 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 pleting.
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'
.