Perché .json () restituisce una promessa?


116

fetch()Recentemente ho scherzato con l' API e ho notato qualcosa che era un po 'bizzarro.

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.datarestituisce un Promiseoggetto. http://jsbin.com/wofulo/2/edit?js,output

Tuttavia, se è scritto come:

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

let iterator = fetch(url);

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

postecco uno standard a Objectcui puoi accedere all'attributo title. http://jsbin.com/wofulo/edit?js,output

Quindi la mia domanda è: perché response.jsonrestituisce una promessa in un oggetto letterale, ma restituisce il valore se appena restituito?


1
Ciò ha senso se si considera che la response.json()promessa potrebbe essere rifiutata se la risposta non è JSON valido.
ssube

1
Il valore viene restituito perché la promessa è stata risolta passando il valore in response.json (). Ora il valore è disponibile nel metodo then.
Jose Hermosilla Rodrigo

Risposte:


168

Perché response.jsonrestituisce una promessa?

Perché ricevi il responsenon appena tutte le intestazioni sono arrivate. La chiamata .json()ti dà un'altra promessa per il corpo della risposta http che deve ancora essere caricata. Vedi anche Perché l'oggetto risposta dall'API di recupero JavaScript è una promessa? .

Perché ottengo il valore se restituisco la promessa dal thengestore?

Perché è così che funzionano le promesse . La capacità di restituire le promesse dalla richiamata e di farle adottare è la loro caratteristica più rilevante, le rende concatenabili senza annidamenti.

Puoi usare

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

o qualsiasi altro approccio per accedere alla promessa precedente risulta in una catena .then () per ottenere lo stato della risposta dopo aver atteso il corpo json.


Sembra strano che non posso semplicemente aspettare che i dati tornino usando una promessa e quando è arrivato convertirli in json? O forse in quel caso potrei usare solo al JSON.parse()posto di res.json()??
Kokodoko

8
@Kokodoko res.json()è fondamentalmente una scorciatoia per res.text().then(JSON.parse). Entrambi attendono i dati usando una promessa e analizzano il json.
Bergi

@Bergi, ciao, scusa ho dovuto affrontare un po 'di confusione, cioè usando quindi (res => res.json ()) inviamo un'altra richiesta per ottenere JSON?
mirzhal

1
@mirzhal No, non ci sono altre richieste. Sta solo leggendo (in modo asincrono!) Il resto della risposta.
Bergi

14

Questa differenza è dovuta al comportamento di Promises più che fetch()specificamente.

Quando una .then()richiamata ne restituisce un'altra Promise, la .then()richiamata successiva nella catena è essenzialmente vincolata a quella Promessa, ricevendone la risoluzione o rifiutando l'adempimento e il valore.

Il secondo frammento potrebbe anche essere stato scritto come:

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

Sia in questa forma che nella tua, il valore di postè fornito dalla Promessa restituita daresponse.json() .


Quando restituisci una pianura Object, però, .then()considera che un risultato positivo e si risolve immediatamente, in modo simile a:

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

postin questo caso è semplicemente il che Objecthai creato, che detiene un Promisenella sua dataproprietà. L'attesa che si adempia quella promessa è ancora incompleta.


7

Inoltre, ciò che mi ha aiutato a capire questo particolare scenario che hai descritto è la documentazione dell'API Promise , in particolare dove spiega come la promessa restituita dal thenmetodo verrà risolta in modo diverso a seconda di ciò che restituisce il gestore fn :

se la funzione handler:

  • restituisce un valore, la promessa restituita da allora viene risolta con il valore restituito come valore;
  • genera un errore, la promessa restituita da allora viene rifiutata con l'errore lanciato come suo valore;
  • restituisce una promessa già risolta, la promessa restituita da allora viene risolta con il valore di quella promessa come valore;
  • restituisce una promessa già rifiutata, la promessa restituita da allora viene rifiutata con il valore di quella promessa come valore.
  • restituisce un altro oggetto di promessa in sospeso, la risoluzione / rifiuto della promessa restituita entro tale termine sarà successiva alla risoluzione / rifiuto della promessa restituita dal gestore. Inoltre, il valore della promessa restituita entro tale data sarà uguale al valore della promessa restituita dal gestore.

5

Oltre alle risposte precedenti, ecco come puoi gestire una risposta della serie 500 dalla tua API in cui ricevi un messaggio di errore codificato in 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
}
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.