1 September 2021

Fetch: Kemajuan Download

Metode fetch memungkinkan untuk melacak kemajuan proses download.

Harap diperhatikan: Saat ini belum ada cara bagi metode fetch untuk melacak kemajuan proses upload. Untuk tujuan tersebut, gunakan XMLHttpRequest, kita akan membahasnya nanti.

Untuk melacak kemajuan download, kita dapat menggunakan properti response.body. Itu adalah ReadableStream – sebuah objek khusus yang menyediakan potongan demi potongan response.body saat didapatkan. Readable streams dideskripsikan pada spesifikasi Streams API.

Tidak seperti response.text(), response.json() dan metode lainnya, response.body memberikan kontrol penuh atas proses pembacaan dan kita dapat menghitung berapa banyak data yang diterima setiap saat.

Berikut adalah sketsa kode yang membaca respon dari response.body:

// sebagai ganti response.json() dan metode lainnya
const reader = response.body.getReader();

// perulangan tidak terbatas ketika proses mengunduh
while (true) {
  // done bernilai benar (true) untuk bagian terakhir
  // value adalah Uint8Array dari bagian bytes
  const { done, value } = await reader.read();

  if (done) {
    break;
  }

  console.log(`Diterima ${value.length} bytes`);
}

Hasil dari pemanggilan await reader.read() adalah objek dengan dua properti:

  • donetrue saat proses pembacaan selesai, jika tidak false.
  • value – larik dengan tipe bytes: Uint8Array.
Tolong dicatat:

Streams API juga mendeskripsikan iterasi asynchronous melalui ReadableStream dengan perulangan for await..of, tetapi itu tidak sepenuhnya didukung (lihat browser issues), sehingga kita menggunakan perulangan while.

Kita menerima potongan respon pada perulangan sampai proses loading selesai, yaitu: sampai done bernilai true.

Untuk mencatat kemajuan, kita hanya perlu setiap value dari fragment yang diterima untuk ditambahkan nilai panjangnya ke penghitung.

Berikut adalah contoh penuh yang menerima respon dan mencatat kemajuan pada console, Langkah-langkah yang dapat diikuti:

// Langkah 1: mulai fetch dan dapatkan reader
let response = await fetch(
  'https://api.github.com/repos/javascript-tutorial/en.javascript.info/commits?per_page=100'
);

const reader = response.body.getReader();

// Langkah 2: dapatkan panjang total (data)
const contentLength = +response.headers.get('Content-Length');

// Langkah 3: Membaca data
let receivedLength = 0; // Menerima banyak bytes saat ini
let chunks = []; // larik potongan biner yang diterima (Meliputi body respon)
while (true) {
  const { done, value } = await reader.read();

  if (done) {
    break;
  }

  chunks.push(value);
  receivedLength += value.length;

  console.log(`Diterima ${receivedLength} dari ${contentLength}`);
}

// Langkah 4: Menyatukan potongan-potongan menjadi satu Uint8Array
let chunksAll = new Uint8Array(receivedLength); // (4.1)
let position = 0;
for (let chunk of chunks) {
  chunksAll.set(chunk, position); // (4.2)
  position += chunk.length;
}

// Langkah 5: Mengubah menjadi string
let result = new TextDecoder('utf-8').decode(chunksAll);

// Selesai
let commits = JSON.parse(result);
alert(commits[0].author.login);

Mari kita jelaskan langkah demi langkah:

  1. Kita menggunakan fetch seperti biasa, tetapi sebagai ganti penggunaan response.json(), kita menggunakan stream reader response.body.getReader(). Perlu dicatat, kita tidak dapat menggunakan kedua metode ini untuk membaca respon yang sama: baik menggunakan reader atau metode respon untuk mendapatkan hasilnya.

  2. Sebelum proses pembacaan, kita bisa mendapatkan keseluruhan panjang respon dari Content-Length header.

    Itu kemungkinan tidak ada untuk permintaan lintas sumber (lihat bagian Fetch: *request Cross-Origin*) dan secara teknis server tidak harus menyetelnya. Tetapi biasanya ada.

  3. Penggil await reader.read() sampai selesai.

    Kita mengumpulkan potongan respon di larik chunks. Itu penting karena setelah respon diterima, kita tidak akan dapat “membaca ulang” menggunakan response.json() atau dengan cara lain (Anda dapat mencobanya dan akan ada galat)

  4. Pada akhirnya, kita memiliki chunks – sebuah larik dari potongan byte ʻUint8Array`. Kita perlu menggabungkannya menjadi satu data tunggal. Sayangnya, tidak ada metode tunggal yang dapat menggabungkannya, jadi ada beberapa kode untuk melakukannya: