web-dev-qa-db-fra.com

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

Je me suis amusé avec la fetch() api récemment, et j'ai 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 objet Promise. http://jsbin.com/wofulo/2/edit?js,output

Cependant, si c'est écrit comme:

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 Object standard auquel vous pouvez accéder à l'attribut title. http://jsbin.com/wofulo/edit?js,output

Ma question est donc la suivante: pourquoi response.json renvoie-t-il une promesse dans un objet littéral, mais renvoie-t-il la valeur s'il vient d'être renvoyé?

85
haveacigaro

Pourquoi response.json renvoie-t-il une promesse?

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

Pourquoi ai-je la valeur si je retourne la promesse du gestionnaire then?

Parce que c'est comme ça que les promesses marchent . La possibilité de retourner des promesses à partir du rappel et de les faire adopter est leur caractéristique la plus pertinente, cela 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 des les approches permettant d'accéder aux promesses précédentes aboutissent à une chaîne .then () pour obtenir l'état de la réponse après avoir attendu le corps json.

129
Bergi

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

Lorsqu'un rappel .then() renvoie un Promise supplémentaire, le prochain rappel .then() de la chaîne est essentiellement lié à cette promesse, recevant sa résolution ou rejetant l'exécution et la valeur.

Le deuxième extrait aurait également pu être écrit comme suit:

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

Dans ce formulaire et le vôtre, la valeur de post est fournie par la promesse renvoyée par response.json().


Lorsque vous renvoyez un Object brut, cependant, .then() considère qu'un résultat est réussi et se résout immédiatement, comme suit:

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

post est dans ce cas simplement la Object que vous avez créée et qui contient une Promise dans sa propriété data. L'attente pour que cette promesse soit remplie est encore incomplète.

12

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

si la fonction de gestionnaire:

  • renvoie une valeur, la promesse renvoyée à ce moment-là est résolue avec la valeur renvoyée comme valeur;
  • renvoie une erreur, la promesse renvoyée à l’époque est rejetée avec l’erreur renvoyée comme valeur;
  • retourne une promesse déjà résolue, la promesse retournée d'ici est résolue avec la valeur de cette promesse comme valeur;
  • renvoie une promesse déjà rejetée, la promesse retournée à cette date est rejetée avec la valeur de cette promesse comme valeur.
  • renvoie un autre objet de promesse en attente, la résolution/le rejet de la promesse retournée à ce moment-là sera postérieur à la résolution/le rejet de la promesse renvoyée par le gestionnaire. En outre, la valeur de la promesse renvoyée à ce moment-là sera la même que la valeur de la promesse renvoyée par le gestionnaire.
7
Gera Zenobi

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