Search code examples
javascriptasynchronouspromisefetch-api

Why does .json() return a promise, but not when it passes through .then()?


I've been messing around with the fetch() api recently, and noticed something which was a bit quirky.

let url = "http://jsonplaceholder.typicode.com/posts/6";

let iterator = fetch(url);

iterator
  .then(response => {
      return {
          data: response.json(),
          status: response.status
      }
  })
  .then(post => document.write(post.data));
;

post.data returns a Promise object. http://jsbin.com/wofulo/2/edit?js,output

However if it is written as:

let url = "http://jsonplaceholder.typicode.com/posts/6";

let iterator = fetch(url);

iterator
  .then(response => response.json())
  .then(post => document.write(post.title));
;

post here is a standard Object which you can access the title attribute. http://jsbin.com/wofulo/edit?js,output

So my question is: why does response.json return a promise in an object literal, but return the value if just returned?


Solution

  • Why does response.json return a promise?

    Because you receive the response as soon as all headers have arrived. Calling .json() gets you another promise for the body of the http response that is yet to be loaded. See also Why is the response object from JavaScript fetch API a promise?.

    Why do I get the value if I return the promise from the then handler?

    Because that's how promises work. The ability to return promises from the callback and get them adopted is their most relevant feature, it makes them chainable without nesting.

    You can use

    fetch(url).then(response => 
        response.json().then(data => ({
            data: data,
            status: response.status
        })
    ).then(res => {
        console.log(res.status, res.data.title)
    }));
    

    or any other of the approaches to access previous promise results in a .then() chain to get the response status after having awaited the json body. Modern version using await (inside an async function):

    const response = await fetch(url);
    const data = await response.json();
    console.log(response.status, data.title);
    

    Also, you might want to check the status (or just .ok) before reading the response, it might not be JSON at all.