2

I need to write a function which makes two chained HTTP calls and act upon the result once the results of the second call are available.

The approach I thought would works is

function getdata() {
  return fetch('https://jsonplaceholder.typicode.com/todos/1')
    .then(r => r.json())
    .then(r => fetch(`https://jsonplaceholder.typicode.com/todos/2`)
      .then(s => s.json())
    )
}
let m = getdata()
m.then(x => console.log(JSON.stringify(x)))

This works fine, the console output is as expected.

I then ported the idea to my actual call, the main difference being that the HTTPS calls are slow. For the sake of mapping the actual error to code, consider a small variation of the code above (with some logging added)

function getdata() {
  return fetch('https://jsonplaceholder.typicode.com/todos/1')
    .then(r => r.json())
    .then(r => fetch(`https://jsonplaceholder.typicode.com/todos/2`)
      .then(s => {
        console.log(s)
        s.json()
      })
    )
}
let m = getdata()
m.then(x => console.log(x))

The console output (from my actual code) is similar to

11:36:56.388 undefined
11:36:56.456 filter-sentinels.js:42 Response {type: "cors", url: …}

In details:

  • the first undefined is from console.log(x)
  • the second line is from console.log(s)

It would seem that the Promise is resolved in the second line, which comes after the first one. This is normal, it is what promises are for.

What I do not understand is why the .then() in m.then(x => console.log(x) )) is executed when the promise is not resolved yet?

A note on the title: what I actually want to understand is whether I can treat a Promise being generated (let m = ...) as something synchronous - in the sense that I can safely apply a then() to it and what happens in the then() will happen when relevant information is known (the HTTP call has returned).

jo_va
  • 11,779
  • 3
  • 20
  • 39
WoJ
  • 19,312
  • 30
  • 122
  • 230
  • 1
    Change `s.json()` to `return s.json()` – Amir Popovich Apr 04 '19 at 10:36
  • each `.then` is returning a promise, except the last one, where `s.json` is not returned. In general, anyway, the code inside the promise callback is called **after** the promise is resolved – briosheje Apr 04 '19 at 10:37

1 Answers1

3

The issue is that there is one Promise in the neseted fetch which is not being chained with the outer ones, the s.json():

.then(s => {
  console.log(s)
  s.json() // <---- This is not being returned; the Promise chain will resolve to undefined
})

You're just declaring the s.json() Promise, but you're not returning it, or using it at all, so the entire getdata() Promise resolves to undefined, once the response from /totos/2 comes back.

Return the final nested .json() call, and it works as expected:

function getdata() {
  return fetch('https://jsonplaceholder.typicode.com/todos/1')
    .then(r => r.json())
    .then(r => fetch(`https://jsonplaceholder.typicode.com/todos/2`)
      .then(s => {
        console.log(s)
        return s.json()
      })
    )
}
m = getdata()
m.then(x => console.log(x) )
CertainPerformance
  • 260,466
  • 31
  • 181
  • 209
  • Thank you. In that case, why does the first code work as expected? – WoJ Apr 04 '19 at 10:39
  • Because the inner `.json` is being (implicitly) returned by the arrow function. (Arrow functions with blocks `{` `}` like in the second code do not ever implicitly return anything, so you need the `return` statement) – CertainPerformance Apr 04 '19 at 10:40
  • https://stackoverflow.com/questions/28889450/when-should-i-use-return-in-es6-arrow-functions – Amir Popovich Apr 04 '19 at 10:41
  • I understand, thanks. I also see that I would be better off (at least from a personal readability perspective) chaining flat `then()` calls. – WoJ Apr 04 '19 at 10:57