Warum gibt .json () ein Versprechen zurück?

115

Ich habe fetch()kürzlich mit der API herumgespielt und etwas bemerkt, das etwas schrullig war.

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.datagibt ein PromiseObjekt zurück. http://jsbin.com/wofulo/2/edit?js,output

Wenn es jedoch wie folgt geschrieben ist:

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

let iterator = fetch(url);

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

postHier ist ein Standard, auf Objectden Sie zugreifen können. http://jsbin.com/wofulo/edit?js,output

Meine Frage lautet also: Warum wird response.jsonein Versprechen in einem Objektliteral zurückgegeben, aber der Wert wird zurückgegeben, wenn er gerade zurückgegeben wird?

Haveacigaro
quelle
1
Dies ist sinnvoll, wenn Sie bedenken, dass das response.json()Versprechen möglicherweise abgelehnt wird, wenn die Antwort nicht gültig ist. JSON.
ssube
1
Der Wert wird zurückgegeben, da das Versprechen aufgelöst wurde und der Wert in response.json () übergeben wurde. Jetzt ist der Wert in der Methode then verfügbar.
Jose Hermosilla Rodrigo

Antworten:

166

Warum gibt response.jsonein Versprechen zurück?

Weil Sie die erhalten, responsesobald alle Header angekommen sind. Wenn .json()Sie anrufen, erhalten Sie ein weiteres Versprechen für den Text der http-Antwort, die noch geladen werden muss. Siehe auch Warum ist das Antwortobjekt von der JavaScript-Abruf-API ein Versprechen? .

Warum erhalte ich den Wert, wenn ich das Versprechen vom thenHandler zurückschicke?

Denn so funktionieren Versprechen . Die Fähigkeit, Versprechen aus dem Rückruf zurückzugeben und sie zu übernehmen, ist ihr wichtigstes Merkmal. Sie macht sie verkettbar, ohne sie zu verschachteln.

Sie können verwenden

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

oder jeder andere Ansatz für den Zugriff auf frühere Versprechen führt zu einer .then () -Kette , um den Antwortstatus zu erhalten, nachdem der json-Körper gewartet hat.

Bergi
quelle
Es scheint seltsam, dass ich nicht einfach warten kann, bis die Daten mit einem Versprechen zurückgegeben werden, und wenn sie angekommen sind, sie in json konvertieren? Oder vielleicht könnte ich in diesem Fall einfach JSON.parse()anstelle von res.json()??
Kokodoko
8
@ Kokodoko ist im res.json()Grunde eine Abkürzung für res.text().then(JSON.parse). Beide warten mit einem Versprechen auf die Daten und analysieren den JSON.
Bergi
@Bergi, hi, tut mir leid, ich hatte einige Verwirrung, das heißt, wenn wir dann (res => res.json ()) verwenden, senden wir eine weitere Anfrage, um JSON zu erhalten?
Mirzhal
1
@mirzhal Nein, es gibt keine andere Anfrage. Es liest nur (asynchron!) Den Rest der Antwort.
Bergi
14

Dieser Unterschied ist mehr auf das Verhalten von Promises zurückzuführen als fetch() spezifisch .

Wenn ein .then()Rückruf einen zusätzlichen zurückgibt Promise, den nächsten.then() Rückruf in der Kette im Wesentlichen an dieses Versprechen gebunden und erhält seine Entschlossenheit oder lehnt Erfüllung und Wert ab.

Das 2. Snippet hätte auch geschrieben werden können als:

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

Sowohl in dieser als auch in Ihrer Form wird der Wert von postdurch das von zurückgegebene Versprechen bereitgestellt response.json().


Wenn Sie jedoch eine Ebene zurückgeben Object, wird dies .then()als erfolgreiches Ergebnis betrachtet und sofort aufgelöst, ähnlich wie bei:

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

postIn diesem Fall handelt es sich einfach um das von ObjectIhnen erstellte Objekt , das ein Promisein seiner dataEigenschaft enthält. Das Warten auf die Erfüllung dieses Versprechens ist noch unvollständig.

Jonathan Lonowski
quelle
7

Was mir auch geholfen hat, dieses von Ihnen beschriebene Szenario zu verstehen, ist die Promise-API- Dokumentation , in der erläutert wird, wie das von der thenMethode zurückgegebene Versprechen je nach dem, was der Handler fn zurückgibt, unterschiedlich aufgelöst wird:

wenn der Handler funktioniert:

  • Gibt einen Wert zurück. Das bis dahin zurückgegebene Versprechen wird mit dem zurückgegebenen Wert als Wert aufgelöst.
  • Wenn ein Fehler ausgegeben wird, wird das bis dahin zurückgegebene Versprechen mit dem ausgelösten Fehler als Wert abgelehnt.
  • Gibt ein bereits aufgelöstes Versprechen zurück. Das bis dahin zurückgegebene Versprechen wird mit dem Wert dieses Versprechens als Wert aufgelöst.
  • Gibt ein bereits abgelehntes Versprechen zurück, wird das bis dahin zurückgegebene Versprechen mit dem Wert dieses Versprechens als Wert abgelehnt.
  • Gibt ein anderes ausstehendes Versprechen zurück, erfolgt die Auflösung / Ablehnung des bis dahin zurückgegebenen Versprechens nach der Auflösung / Ablehnung des vom Bearbeiter zurückgegebenen Versprechens. Außerdem entspricht der Wert des bis dahin zurückgegebenen Versprechens dem Wert des vom Handler zurückgegebenen Versprechens.
Gera Zenobi
quelle
5

Zusätzlich zu den obigen Antworten erfahren Sie hier, wie Sie mit einer Antwort der 500er-Serie von Ihrer API umgehen können, bei der Sie eine in json codierte Fehlermeldung erhalten:

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
quelle