Mengapa .json () mengembalikan promise?

116

Saya telah bermain-main dengan fetch()api baru-baru ini, dan melihat sesuatu yang agak aneh.

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.datamengembalikan sebuah Promiseobjek. http://jsbin.com/wofulo/2/edit?js,output

Namun jika ditulis sebagai:

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

let iterator = fetch(url);

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

postberikut adalah standar Objectyang dapat Anda akses atribut judul. http://jsbin.com/wofulo/edit?js,output

Jadi pertanyaan saya adalah: mengapa response.jsonmengembalikan janji dalam objek literal, tetapi mengembalikan nilainya jika baru saja dikembalikan?

hasacigaro
sumber
1
Ini masuk akal jika Anda menganggap bahwa response.json()janji mungkin ditolak jika respons JSON tidak valid.
ssube
1
Nilai dikembalikan karena janji telah diselesaikan dengan meneruskan nilai di response.json (). Sekarang nilainya tersedia di metode itu.
Jose Hermosilla Rodrigo

Jawaban:

168

Mengapa response.jsonmembalas janji?

Karena Anda menerima responsesegera setelah semua header telah tiba. Menelepon .json()memberi Anda janji lain untuk isi respons http yang belum dimuat. Lihat juga Mengapa objek respons dari JavaScript fetch API sebuah janji? .

Mengapa saya mendapatkan nilai jika saya mengembalikan promise dari thenpawang?

Karena begitulah cara kerja janji . Kemampuan untuk mengembalikan promise dari callback dan membuatnya diadopsi adalah fitur mereka yang paling relevan, yang membuatnya dapat dirantai tanpa bersarang.

Kamu bisa memakai

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

atau pendekatan lain apa pun untuk mengakses hasil promise sebelumnya dalam rantai .then () untuk mendapatkan status respons setelah menunggu badan json.

Bergi
sumber
Tampaknya aneh bahwa saya tidak bisa hanya menunggu datanya kembali menggunakan Promise, dan ketika sudah tiba, konversikan ke json? Atau mungkin dalam hal ini saya bisa menggunakan JSON.parse()daripada res.json()??
Kokodoko
8
@Kokodoko res.json()pada dasarnya adalah jalan pintas untuk res.text().then(JSON.parse). Keduanya menunggu data menggunakan promise dan mengurai json.
Bergi
@Bergi, hai, maaf saya mengalami kebingungan, yaitu dengan menggunakan kemudian (res => res.json ()) kami mengirim permintaan lain untuk mendapatkan JSON?
mirzhal
1
@mirz_ Tidak, tidak ada permintaan lain. Itu hanya membaca (secara tidak sinkron!) Sisa respons.
Bergi
14

Perbedaan ini disebabkan oleh perilaku Janji lebih dari fetch()secara spesifik.

Saat .then()callback mengembalikan tambahan Promise, .then()callback berikutnya dalam rantai pada dasarnya terikat ke Promise itu, menerima penyelesaian atau penolakan pemenuhan dan nilainya.

Cuplikan ke-2 juga dapat ditulis sebagai:

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

Baik dalam bentuk ini maupun milik Anda, nilai postdiberikan oleh Janji yang dikembalikan dari response.json().


Namun, ketika Anda mengembalikan sebuah dataran Object, .then()menganggap itu sebagai hasil yang sukses dan segera terselesaikan dengan sendirinya, mirip dengan:

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

postdalam hal ini hanya ObjectAnda yang dibuat, yang memegang a Promisedi datapropertinya. Penantian janji itu untuk dipenuhi masih belum lengkap.

Jonathan Lonowski
sumber
7

Selain itu, yang membantu saya memahami skenario khusus yang Anda jelaskan ini adalah dokumentasi Promise API , yang secara khusus menjelaskan bagaimana janji yang dikembalikan oleh thenmetode akan diselesaikan secara berbeda bergantung pada apa yang dikembalikan oleh penangan :

jika fungsi penangan:

  • mengembalikan nilai, janji yang dikembalikan saat itu diselesaikan dengan nilai yang dikembalikan sebagai nilainya;
  • melempar kesalahan, janji yang dikembalikan saat itu akan ditolak dengan kesalahan yang dilemparkan sebagai nilainya;
  • mengembalikan janji yang sudah diselesaikan, janji yang dikembalikan saat itu diselesaikan dengan nilai janji itu sebagai nilainya;
  • mengembalikan janji yang sudah ditolak, janji yang dikembalikan saat itu akan ditolak dengan nilai janji itu sebagai nilainya.
  • mengembalikan objek promise lain yang menunggu keputusan, resolusi / penolakan dari promise yang dikembalikan saat itu akan mengikuti resolusi / penolakan dari promise yang dikembalikan oleh penangan. Selain itu, nilai promise yang dikembalikan saat itu akan sama dengan nilai promise yang dikembalikan oleh penangan.
Gera Zenobi
sumber
5

Selain jawaban di atas, berikut adalah bagaimana Anda dapat menangani respons seri 500 dari api Anda di mana Anda menerima pesan kesalahan yang dikodekan dalam 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.dll
sumber