¿Por qué .json() devuelve una promesa, pero no cuando pasa por .then()?

Resuelto haveacigaro asked hace 8 años • 5 respuestas

Estuve jugando con la fetch()API recientemente y noté algo que era un poco peculiar.

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.datadevuelve un Promiseobjeto. http://jsbin.com/wofulo/2/edit?js,salida

Sin embargo, si se escribe como:

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

let iterator = fetch(url);

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

postAquí hay un estándar Objectal que puede acceder mediante el atributo de título. http://jsbin.com/wofulo/edit?js,salida

Entonces mi pregunta es: ¿por qué response.jsondevuelve una promesa en un objeto literal, pero devuelve el valor si se acaba de devolver?

haveacigaro avatar Jun 01 '16 03:06 haveacigaro
Aceptado

¿Por qué response.jsondevuelve una promesa?

Porque recibirás el responsetan pronto como lleguen todos los encabezados. Al llamar, .json()obtendrá otra promesa para el cuerpo de la respuesta http que aún no se ha cargado. Consulte también ¿Por qué el objeto de respuesta de la API de recuperación de JavaScript es una promesa? .

¿Por qué obtengo el valor si devuelvo la promesa del thencontrolador?

Porque así es como funcionan las promesas . La capacidad de devolver promesas de la devolución de llamada y hacer que se adopten es su característica más relevante, ya que las hace encadenables sin anidar.

Puedes usar

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

o cualquier otro de los enfoques para acceder a la promesa anterior da como resultado una cadena .then() para obtener el estado de la respuesta después de haber esperado el cuerpo json. Versión moderna que usa await(dentro de una asyncfunción):

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

Además, es posible que desees verificar el estado (o simplemente .ok) antes de leer la respuesta; es posible que no sea JSON en absoluto.

Bergi avatar May 31 '2016 20:05 Bergi

Esta diferencia se debe al comportamiento de Promises más que fetch()específicamente.

Cuando una .then()devolución de llamada devuelve un adicional Promise, la siguiente .then()devolución de llamada en la cadena está esencialmente vinculada a esa Promesa, recibiendo su resolución o rechazo, cumplimiento y valor.

El segundo fragmento también podría haberse escrito como:

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

Tanto en este formulario como en el suyo, el valor de lo postproporciona la Promesa devuelta por response.json().


Sin embargo, cuando devuelve un Plain Object, .then()considera que es un resultado exitoso y se resuelve por sí solo de inmediato, de manera similar a:

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

posten este caso es simplemente el que Objectusted creó, que tiene a Promiseen su datapropiedad. La espera para que se cumpla esa promesa aún está incompleta.

Jonathan Lonowski avatar May 31 '2016 20:05 Jonathan Lonowski

Además de las respuestas anteriores, aquí se explica cómo podría manejar una respuesta de la serie 500 de su API donde recibe un mensaje de error codificado en json:

function callApi(url) {
  return fetch(url)
    .then(response => {
      if (response.ok) {
        return response.json().then(response => ({ response }));
      }

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

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

const { response, error } = callApi(url);
if (response) {
  // handle json decoded response
} else {
  // handle json decoded 500 series response
}
jcroll avatar Sep 21 '2016 16:09 jcroll