-1

I have the following fetch() api but the catch blocks aren't working correctly. The error message I get is:

SyntaxError: Unexpected token < in JSON at position 0 undefined

but what I'm expecting is:

something went wrong null

here's the api:

const getBtn = document.getElementById('get-btn')
const postBtn = document.getElementById('post-btn')


const sendHttpRequest = (method, url, data) => {
    return fetch(url, {
        method: method,
        body: JSON.stringify(data),
        headers: data ? {'Content-Type': 'application/json'} : {}
    })
        .then(response => {
            console.log(response.status)
            if(response.status >= 400 || response == null){
                return response.json()
                    .then(errResData => {
                        const error = new Error('something went wrong')
                        error.data = errResData
                        throw error;
                    })
            }
            return response.json()
    })
}

const getData = () =>{
    sendHttpRequest('GET','http://localhost/async/fetch/data.jsonx')
        .then(responseData => {
            console.log(responseData)
        })
        .catch(err =>{
            console.log(err,err.data)
        })

}

const sendData = () =>{
    sendHttpRequest('POST','http://localhost/async/fetch/data.phpx',{
        email: 'someemail@gmail.com',
        password: 'compas'
    })
        .then(responseData => {
            console.log(responseData)
        })
        .catch(err => {
            console.log(err,err.data)
        })
}


getBtn.addEventListener('click',getData)
postBtn.addEventListener('click',sendData)
DCR
  • 14,737
  • 12
  • 52
  • 115
  • `.then(errResData => {` you need `.catch(errResData => {` if you want to capture errors... – CertainPerformance May 31 '21 at 22:06
  • 1
    could it be a 404 and instead of it returning json like the code is expecting, its returning html? normally the first char in a html document is `<`, hence the error. (though looking at data.phpx, it could be ` – Lawrence Cherone May 31 '21 at 22:08
  • 2
    I would guess that `response.json()` fails and you never `catch` that one – Dominik May 31 '21 at 22:09
  • See what you have at http://localhost/async/fetch/data.jsonx, I'm pretty sure there's some HTML in there instead of JSON. – Robo Robok May 31 '21 at 22:11
  • @robo Robok the actual url is localhost/async/fetch/data.json or data.php. I added an x to both to create a 404 error – DCR May 31 '21 at 22:13
  • @certain performance response.json() generates a promise. so I should be at the catch in the get-data() or send-data() functions – DCR May 31 '21 at 22:15
  • @DCR You *are* catching the error of that promise there. It's just that `response.json()` failed to parse the non-JSON response, so your `then` handler that would've constructed a custom error never ran. – Bergi May 31 '21 at 22:17

2 Answers2

1

In order to see if a body is parseable as JSON, you need to call .json on the Promise. That will return a Promise that either resolves to the parsed value, or will throw due to the body not being parseable.

If it isn't parseable, .thens connected to it won't run; return response.json().then will not work if the body isn't parseable, so the interpreter never gets to new Error('something went wrong').

.then(response => {
    console.log(response.status)
    if(response.status >= 400 || response == null){
        return response.json()
            .then(errResData => {
                const error = new Error('something went wrong')
                error.data = errResData
                throw error;
            })
    }
    return response.json()

should be

.then(response => {
    console.log(response.status)
    if(response.status >= 400 || response == null){
        return response.json()
            .catch(errResData => {
                const error = new Error('something went wrong')
                error.data = errResData
                throw error;
            })
    }
    return response.json()

if the non-parseable response will always fulfill the condition response.status >= 400 || response == null.

The throw error inside the .catch in the edited code will result in the Promise rejecting, so getData's .catch will see the error.

CertainPerformance
  • 356,069
  • 52
  • 309
  • 320
  • I follow what you are suggesting but believe you may have overlooked a potential problem. What happens if we have an error but response.json() returns. I think we need both a .then and a .catch – DCR May 31 '21 at 23:24
  • What do you mean, `but response.json() returns`? If, per your question, the error message you want to catch here is `SyntaxError: Unexpected token < in JSON at position 0 undefined`, then adding `.catch` instead of `.then` will properly catch the unparseable JSON. – CertainPerformance May 31 '21 at 23:31
  • yes, that's true but only because response.json() didn't work. suppose the response back was null, throw error would never be invoked cause it's in a catch not a then – DCR May 31 '21 at 23:51
  • `.json` didn't work only because the response wasn't JSON-parseable. `fetch` can't ever return `null` anyway - it'll always be a `Response` type object. If you want to check that the parsed result isn't null, do that in *another* `.then` after calling `.json` - but that's pretty weird, and should probably be handled by the caller instead – CertainPerformance May 31 '21 at 23:59
0

If you want to catch an error from a Promise, you should use .catch() instead of .then()

Evert
  • 93,428
  • 18
  • 118
  • 189