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:
done
–true
saat proses pembacaan selesai, jika tidakfalse
.value
– larik dengan tipe bytes:Uint8Array
.
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:
-
Kita menggunakan
fetch
seperti biasa, tetapi sebagai ganti penggunaanresponse.json()
, kita menggunakan stream readerresponse.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. -
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.
-
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” menggunakanresponse.json()
atau dengan cara lain (Anda dapat mencobanya dan akan ada galat) -
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:- Kita membuat
chunksAll = new Uint8Array (receivedLength)
– larik dengan tipe yang sama dengan panjang gabungan. - Kemudian gunakan metode
.set (chunk, position)
untuk menyalin setiapchunk
satu demi satu di dalamnya.
- Kita membuat
-
Kita mendapatkan hasilnya di
chunksAll
. Ini adalah larik byte, bukan string.Untuk membuat string, kita perlu menafsirkan byte ini. TextDecoder bawaan dapat melakukan hal itu. Lalu kita bisa
JSON.parse
, jika diperlukan.Bagaimana jika kita membutuhkan konten biner, bukan string? Itu bahkan lebih sederhana. Ganti langkah 4 dan 5 dengan satu baris yang membuat
Blob
dari semua potongan:let blob = new Blob(chunks);
Pada akhirnya kita memiliki hasil (sebagai string atau blob, apa pun yang Anda inginkan), dan pelacakan kemajuan dalam prosesnya.
Sekali lagi, harap diperhatikan, itu bukan untuk kemajuan upload (sekarang belum ada cara dengan fetch
), hanya untuk kemajuan download.
Dan juga, jika ukurannya tidak diketahui, kita harus memeriksa acceptLength
di loop dan menghentikannya setelah mencapai batas tertentu. Sehingga chunks
tidak akan melebihkan memori.
komentar
<code>
, untuk beberapa baris – bungkus dengan tag<pre>
, untuk lebih dari 10 baris – gunakan sandbox (plnkr, jsbin, < a href='http://codepen.io'>codepen…)