7

I am trying to use the Fetch API. It seems from examples that a GET request needs one then to parse the response somehow.

Currently I am doing this

fetch(url)
    .then(response => response.json())
    .then(response => {
        console.log(response);  
    });

However that first then seems like a boilerplate. I tried to avoid it, for example:

fetch(url)
    .then(response => {
        console.log(response.json());  
    });

But this logs me a pending Promise with status resolved.

I read other questions on this topic and read a bit about promises, but I couldn't understand if it's possible to combine it in a single then (if so - how?) or not.

For example, two of the answers here point out that

There is no need to use more than one '.then'

and

there is no good reason to have two .then() handlers as the code from each could have been combined into a single .then() handler

But I couldn't get that example to actually work - I still got a promise :)

On the contrary, the accepted anwser here explains that .then actually does something to the result (extracts the returned from promise), but I couldn't unserstand if I can somehow do that myself, say response.json().then() or response.json().getVal() or is the double-then syntax the only way.

Džuris
  • 2,115
  • 3
  • 27
  • 55

3 Answers3

14

It's quite simple: when you dispatch the a fetch() request, it returns a promise containing the response. That is resolved by the first .then(). Resolving this first promise actually returns Response.

Now this is the tricky part: the methods that read the body of the response, be it .json(), .text(), .blob().... all return promises. This means that you will need to resolve the second promise in order to get the parsed response.

The flow looks like this:

  1. Make a fetch() request, and it returns a Promise of type Response
  2. When you attempt to resolve the content of the Response, it will return a second Promise, whose type depends on the method you use (e.g. .json() returns an object, .text() returns string, .blob() returns Blob).
  3. Resolve the second Promise, and you get your actual parsed response body

p/s: If you're not using fetch() in a top-level context (as of the time of writing top-level await is still not a thing), then you can use async/await to make your code a little more readable:

const response = await fetch(url);
const content = await response.json();
console.log(content);
Terry
  • 63,248
  • 15
  • 96
  • 118
  • 1
    Ah, the tricky part is exactly what was a bit too tricky to get it myself :) So it's either chaining or nesting `response.json().then(r => {console.log(r)})`. – Džuris Aug 29 '18 at 19:09
  • @Džuris Precisely. I would not encourage nesting because it defeats the purpose of daisy-chaining promises (which is a lot prettier/more readable). – Terry Aug 29 '18 at 19:23
  • 1
    Thanks for the above clarification, @Terry. I'm a bit confused on 1 point. Does the Response in the first Promise contain the HTTP response body from the 'get go'. I'm wondering why it was designed in such a way that you need to fetch another Promise to get at the body. What's it actually doing, under the hood? Tx :) – Peter Dec 10 '20 at 07:38
  • 1
    I like this solution, but am I correct in assuming that doing it this way you lose the ability to use the "catch"? Or can it still be done? And why did they make it this way instead of just returning the response object ready to go? Why the need for the French Taunter? Is there something you are going to do with the response other than read it? – KWallace Jun 23 '22 at 17:55
2

The first promise returned by fetch can be useful in some cases. If you want to avoid boilerplate, you could simply create your own function:

function fetch_json(url, opts) {
    return fetch(url, opts)
        .then(resp => resp.json());
}

fetch_json(your_url)
    .then(json => console.log(json));
Petr Broz
  • 8,891
  • 2
  • 15
  • 24
  • This is what I would recommend. It avoids repeating the same code over and over again. When you just want the JSON, you can just use this one utility function. – jfriend00 Aug 29 '18 at 22:51
2

These days I am using the async/await syntax which is the same thing, but looks less like a boilerplate to me.

const response = await fetch(url)
const data = await response.json()

console.log(data)
Džuris
  • 2,115
  • 3
  • 27
  • 55