Pourquoi .json () renvoie-t-il une promesse?

116

J'ai joué avec le fetch() récemment déconné API et remarqué quelque chose d'un peu bizarre.

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

Cependant s'il s'écrit:

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

let iterator = fetch(url);

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

post voici un standard Object auquel vous pouvez accéder à l'attribut title. http://jsbin.com/wofulo/edit?js,output

Donc ma question est: pourquoi response.json renvoie une promesse dans un objet littéral, mais renvoie la valeur si elle vient d'être renvoyée?

Haveacigaro
la source
1
Cela a du sens lorsque vous considérez que la response.json()promesse peut être rejetée si la réponse n'est pas JSON valide.
ssube
1
La valeur est renvoyée car la promesse a été résolue en passant la valeur dans response.json (). Maintenant, la valeur est disponible dans la méthode then.
Jose Hermosilla Rodrigo

Réponses:

168

Pourquoi response.jsonretourne- t-il une promesse?

Parce que vous recevez le responsedès que tous les en-têtes sont arrivés. L'appel .json()vous donne une autre promesse pour le corps de la réponse http qui n'a pas encore été chargée. Voir aussi Pourquoi l'objet de réponse de l'API JavaScript Fetch est-il une promesse? .

Pourquoi est-ce que j'obtiens la valeur si je retourne la promesse du thengestionnaire?

Parce que c'est ainsi que fonctionnent les promesses . La possibilité de renvoyer les promesses du rappel et de les faire adopter est leur caractéristique la plus pertinente, elle les rend chaînables sans imbrication.

Vous pouvez utiliser

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

ou toute autre approche pour accéder à la promesse précédente entraîne une chaîne .then () pour obtenir l'état de la réponse après avoir attendu le corps json.

Bergi
la source
Il semble étrange que je ne puisse pas simplement attendre que les données reviennent en utilisant une promesse, et quand elles sont arrivées, les convertir en json? Ou peut-être que dans ce cas, je pourrais simplement utiliser à la JSON.parse()place de res.json()??
Kokodoko
8
@Kokodoko res.json()est essentiellement un raccourci pour res.text().then(JSON.parse). Les deux attendent les données à l'aide d'une promesse et analysent le json.
Bergi
@Bergi, salut, désolé j'ai été confronté à une certaine confusion, c'est-à-dire qu'en utilisant then (res => res.json ()) nous envoyons une autre demande pour obtenir JSON?
mirzhal
1
@mirzhal Non, il n'y a pas d'autre demande. Il lit simplement (de manière asynchrone!) Le reste de la réponse.
Bergi
14

Cette différence est due au comportement des promesses plus que fetch() spécifiquement.

Lorsqu'un .then()rappel renvoie un supplémentaire Promise, le suivant.then() rappel dans la chaîne est essentiellement lié à cette promesse, recevant sa résolution ou son rejet et sa valeur.

Le 2ème extrait de code aurait également pu être écrit comme suit:

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

Dans ce formulaire comme dans le vôtre, la valeur de postest fournie par la promesse renvoyée par response.json().


Lorsque vous retournez un plaine Object, cependant, .then()considère que le résultat est réussi et se résout immédiatement, semblable à:

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

postdans ce cas, c'est simplement le que Objectvous avez créé, qui détient un Promisedans sa datapropriété. L’attente que cette promesse se réalise est encore incomplète.

Jonathan Lonowski
la source
7

En outre, ce qui m'a aidé à comprendre ce scénario particulier que vous avez décrit est la documentation de l' API Promise , en particulier où elle explique comment la promesse retournée par la thenméthode sera résolue différemment en fonction de ce que le gestionnaire fn renvoie:

si la fonction de gestionnaire:

  • renvoie une valeur, la promesse renvoyée par puis est résolue avec la valeur retournée comme valeur;
  • renvoie une erreur, la promesse renvoyée est alors rejetée avec l'erreur lancée comme valeur;
  • renvoie une promesse déjà résolue, la promesse retournée est alors résolue avec la valeur de cette promesse comme valeur;
  • renvoie une promesse déjà rejetée, la promesse renvoyée est alors rejetée avec la valeur de cette promesse comme valeur.
  • retourne un autre objet de promesse en attente, la résolution / le rejet de la promesse retournée d'ici là sera postérieure à la résolution / le rejet de la promesse retournée par le gestionnaire. En outre, la valeur de la promesse renvoyée d'ici là sera la même que la valeur de la promesse renvoyée par le gestionnaire.
Gera Zenobi
la source
5

En plus des réponses ci-dessus, voici comment vous pouvez gérer une réponse de la série 500 de votre API où vous recevez un message d'erreur encodé 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
la source