1

I know this can be solved by writing all codes to async-await style, then can simply write let text = await res.text(); then try catch the JSON.parse(text) and then do decision.
But here I just want to know if there is any way we can achieve that in .then/.catch style.

Consider the below code:

async function test() { try { let n = await fetch("https://stackoverflow.com") .then(res => { return res.json() }) .then(data => data.results.length) .catch(e => { console.error("Catch 2", e) }) } catch (e) { console.error("Catch 3", e) } } 

if we execute this function in the browser devtools(F12) with await test(), then there will be an error catch by the "Catch 2" clause. But in the error detail we can only see some logs like JSON parse error. We cannot see the full text of the response body. Is there any way that can get the text when the JSON parsing failed?

5
  • 1
    There's no reason to mix then and async/await syntax here. You're also catching the error in the try but never throwing it again so the catch block is never called. Also, you might want .text if you are fetching a source which does not return json Commented Jul 21, 2022 at 9:37
  • "I know this can be solved by writing async-await style code" Can it? A Response's body can only be consumed once. If you wish to do that, either you'd clone the Response before consuming its body, either you start by reading it as text and convert to JSON manually. Commented Jul 21, 2022 at 9:42
  • Sorry, for the "I know this can be solved by writing async-await style code", I didn't explain well enough. I update the description. Commented Jul 21, 2022 at 9:48
  • So your question becomes just how to convert const text = await resp.text(); try { return JSON.parse(text); } catch(err) { return text; } to a chain of Promises? What prevents you to have the exact same try catch in your .then callback? It's all sync after .text() Commented Jul 21, 2022 at 9:52
  • @Kaiido you're right, nothing preventing me to do that.we got the solution. thank you! Commented Jul 21, 2022 at 9:58

3 Answers 3

2

Your best bet is to look at the response in your devtools' network tab. That will show you the full response.

But if you want to do it in code, you can separate reading the response from parsing it by using the text method instead of the json method, then parsing the text yourself.

The parsing error may be down to the fact you aren't checking for HTTP success. As I noted on my old anemic blog here, fetch only rejects its promise on network errors, not HTTP errors (like 404, 500, etc.). To check for HTTP success, look at the ok or status properties.

Here's the minimal-changes version separating reading the response from parsing it, and checking for HTTP success before reading it at all:

async function test() { try { let n = await fetch("https://stackoverflow.com") .then((res) => { if (!res.ok) { // *** throw new Error(`HTTP error ${res.status}`); // *** } // *** return res.text(); // *** }) .then((text) => { // *** you can look at `text` here in a debugger, or // *** log it, save it, etc., before parsing below // *** (which might throw an error) try { const data = JSON.parse(text); // *** return data.results.length; } catch (error) { console.error("Parsing error", e); console.error("Text we were parsing:", text); } }) .catch((e) => { console.error("Catch 2", e); }); // ...do something with `n`... } catch (e) { console.error("Catch 3", e); } } 

But a couple of things there:

  1. I wouldn't mix async/await with explicit promise callbacks like that.

  2. With that and with your original code, errors will result in n receive the value undefined, because the catch handlers (and my new try/catch block in the then handler) don't return anything.

Instead:

async function test() { try { const res = await fetch("https://stackoverflow.com"); if (!res.ok) { throw new Error(`HTTP error ${res.status}`); } const text = await res.text(); // *** you can look at `text` here in a debugger, or // *** log it, save it, etc., before parsing below // *** (which might throw an error) try { const data = JSON.parse(text); const n = data.results.length; // ...do something with `n`... } catch (error) { console.error("Parsing error", e); console.error("Text we were parsing:", text); } } catch (e) { console.error("Catch 3", e); } } 

Or if you want to respond differently to the parsing error, wrap that bit in a try/catch, etc.

Sign up to request clarification or add additional context in comments.

5 Comments

That's the correct solution, but currently text is inaccessible. Maybe wrap the JSON.parse in try-catch?
@Kaiido - I was sort of assuming the user stepping through the code, but that's not a bad idea...
thanks for you answer. For your first code block (which is the style I want), when the parsing failed, we still cannot see the full text in the catch clause and show it in error logs. Do you have any idea?
@Kenneth - As with anything like that, just wrap the JSON.parse in a try/catch and log the text if the result. I'll add it to the above.
@T.J.Crowder oh, you're right, I forgot that JSON.parse is sync code. thank you!
0

You shouldn't confuse the catch which catching errors in the fetch function itself - with the response errors

fetch("/developer.mozilla.org") .then(res => { if (!res.ok) { console.log("there was an error also here") <= this code also runs console.log("response is", res); } return res.json() }) .then(data => data.results.length) .catch(e => { console.error("Catch 2", e); }) 

In your case, you tried converting data -> JSON w/o success, it failed and dropped to the "catch" section. but to inspect the response - you can dump it in the first section above where I added res.ok

1 Comment

Hi, Ricky, thanks for your answer. I know normally we can check the status code first. the code here is just for discuss the error handling solution in promise style code. so please ignore the http status code.
-1

I believe you could do something like this when using promise style Javascript:

const fetchDataPromise = () => { fetch('https://stackoverflow.com').then((res) => { res.json().then((jsonData) => { console.log(jsonData) }).catch((err) => { console.error(err) res.text().then((rawData) => { console.log(rawData) }).catch((err) => console.error(err)) }) }) } 

Also more intuitive approach would be to use async/await (the trade-off is that you will have to do the API call again):

const fetchData = async () => { try { const res = await fetch('https://stackoverflow.com') const jsonData = await res.json() console.log(jsonData) } catch (err) { try { console.error(err) const res = await fetch('https://stackoverflow.com') const rawData = await res.text() console.log(rawData) } catch (rawError) { console.error(rawError) } } } 

2 Comments

for the promise style code block, I think the res.text() may fail due to second call the the response stream.
Hi Darknet, you dont have to do the call twice, basically you can do await res.clone().json() and then just use the res.text()

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.