5

I started with JS and actually like the asynchronous aspects (coming from Python) but I am not sure why some functions return a Promise. Specifically, the following code using fetch makes me wonder about what is returned by json():

fetch('https://freegeoip.net/json/8.8.8.8')
  .then((response) => {
    return response.json()
  })
  .then((json) => {
    Object.keys(json).forEach((key) => {
      console.log("got " + key)
    })
  })

Streaming aside, the HTTP response we get after a GET is a block of text, which is later interpreted by the client to extract the headers, body and other interesting elements - as part of the analysis of the HTTP content.

The point is that this block of text comes in one piece, so the first then() already has the whole response - why is the parsing of the JSON body an asynchronous operation, different from the forEach in the second then()?

In other words, why couldn't I have the followng code working?

fetch('https://freegeoip.net/json/8.8.8.8')
  .then((response) => {
    Object.keys(response.json()).forEach((key) => {
      console.log("got " + key)
    })
  })

Note: please try to disable your adblocker if the first code does not run correctly (with a ERR_BLOCKED_BY_CLIENT). The second one is intentionnaly incorrect.

WoJ
  • 27,165
  • 48
  • 180
  • 345
  • 1
    "*the HTTP response we get after a GET is a block of text*" - no. It's a stream. – Bergi Dec 19 '17 at 08:31
  • @Bergi: in such a case, everything is a stream (of packets, bytes, etc.). A `GET` brings in a finished block of text in one piece (real streaming aside, where the data flow does not end). In the same vein, one could not speak about 'reading in a file to memory' becuse it is technically streaming. – WoJ Dec 19 '17 at 08:36
  • The point of streaming is that it can bring multiple blocks of text - for example, the header can already be processed while the body is not yet finished. And that's exactly what happens here. – Bergi Dec 19 '17 at 08:39

2 Answers2

3

Your second snippet doesn't work because response.json() aka body.json() doesn't resolve instantly.

This is because body.JSON() streams and returns a Response using a Promise asynchronously; which must then be captured by a then() callback in order to read / manipulate.

Such is the nature of Promises.

However, such a syntactic flow can still be achieved by leveraging async await.

fetch('https://freegeoip.net/json/8.8.8.8')
.then(async (response) => {
  Object.keys(await response.json()).forEach((key) => {
    console.log("got " + key)
  })
})
0

You're right - both snippets do the same. In first snippet, following part:

.then((response) => {
  return response.json()
})

Is just handling the response in first step and passing result through. This simple case doesn't need to split it into two steps so you can easily use second example.

It can be useful in some cases.

hsz
  • 148,279
  • 62
  • 259
  • 315