¿Por qué .json () devuelve una promesa?

115

He estado 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,output

Sin embargo, si está escrito 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 al Objectque puede acceder el atributo de título. http://jsbin.com/wofulo/edit?js,output

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

haveacigaro
fuente
1
Esto tiene sentido si considera que la response.json()promesa podría rechazarse si la respuesta no es JSON válido.
ssube
1
El valor se devuelve porque la promesa se ha resuelto pasando el valor en response.json (). Ahora el valor está disponible en el método.
Jose Hermosilla Rodrigo

Respuestas:

167

¿Por qué response.jsondevuelve una promesa?

Porque recibe el responsetan pronto como hayan llegado todos los encabezados. Al llamar, .json()obtiene 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, 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 enfoque para acceder a la promesa anterior da como resultado una cadena .then () para obtener el estado de respuesta después de haber esperado el cuerpo json.

Bergi
fuente
Parece extraño que no pueda simplemente esperar a que los datos regresen usando una Promesa, y cuando llegue, ¿convertirlos a json? ¿O quizás en ese caso podría usar en JSON.parse()lugar de res.json()??
Kokodoko
8
@Kokodoko res.json()básicamente es un atajo para res.text().then(JSON.parse). Ambos esperan los datos usando una promesa y analizan el json.
Bergi
@Bergi, hola, lo siento, enfrenté una cierta confusión, es decir, al usar entonces (res => res.json ()) enviamos otra solicitud para obtener JSON.
mirzhal
1
@mirzhal No, no hay otra solicitud. Simplemente está leyendo (¡asincrónicamente!) El resto de la respuesta.
Bergi
14

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 de 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 postlo proporciona la Promesa devuelta response.json().


Sin Objectembargo, cuando devuelve un simple , .then()considera que es un resultado exitoso y se resuelve automáticamente, 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 Objectque creó, que tiene un Promiseen su datapropiedad. La espera para que se cumpla esa promesa es aún incompleta.

Jonathan Lonowski
fuente
7

Además, lo que me ayudó a comprender este escenario particular que describiste es la documentación de la API de Promise , específicamente donde explica cómo la promesa devuelta por el thenmétodo se resolverá de manera diferente dependiendo de lo que devuelva el controlador fn :

si la función del controlador:

  • devuelve un valor, la promesa devuelta se resuelve con el valor devuelto como su valor;
  • arroja un error, la promesa devuelta luego se rechaza con el error arrojado como su valor;
  • devuelve una promesa ya resuelta, la promesa devuelta entonces se resuelve con el valor de esa promesa como su valor;
  • devuelve una promesa ya rechazada, la promesa devuelta luego se rechaza con el valor de esa promesa como su valor.
  • devuelve otro objeto de promesa pendiente, la resolución / rechazo de la promesa devuelta en ese momento será posterior a la resolución / rechazo de la promesa devuelta por el administrador. Además, el valor de la promesa devuelta para entonces será el mismo que el valor de la promesa devuelta por el controlador.
Gera Zenobi
fuente
5

Además de las respuestas anteriores, aquí se muestra cómo puede 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
fuente