Načítání:Průběh stahování

fetch metoda umožňuje sledovat stahování pokrok.

Poznámka:v současné době neexistuje žádný způsob pro fetch sledovat nahrávání pokrok. Pro tento účel použijte XMLHttpRequest, budeme se jím zabývat později.

Ke sledování průběhu stahování můžeme použít response.body vlastnictví. Je to ReadableStream – speciální předmět, který poskytuje tělo kousek po kousku, jak to jde. Čitelné streamy jsou popsány ve specifikaci Streams API.

Na rozdíl od response.text() , response.json() a další metody, response.body poskytuje plnou kontrolu nad procesem čtení a můžeme spočítat, kolik se spotřebuje v každém okamžiku.

Zde je náčrt kódu, který čte odpověď z response.body :

// instead of response.json() and other methods
const reader = response.body.getReader();

// infinite loop while the body is downloading
while(true) {
  // done is true for the last chunk
  // value is Uint8Array of the chunk bytes
  const {done, value} = await reader.read();

  if (done) {
    break;
  }

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

Výsledek await reader.read() call je objekt se dvěma vlastnostmi:

  • done true po dokončení čtení, jinak false .
  • value – typované pole bajtů:Uint8Array .
Poznámka:

Streams API také popisuje asynchronní iteraci přes ReadableStream s for await..of smyčka, ale zatím není široce podporována (viz problémy s prohlížečem), takže používáme while smyčka.

Ve smyčce dostáváme bloky odpovědí, dokud neskončí načítání, to znamená:do done se změní na true .

Abychom zaznamenali průběh, potřebujeme pro každý přijatý fragment pouze value přidat jeho délku k počítadlu.

Zde je úplný pracovní příklad, který získá odpověď a zaznamená průběh v konzole, další vysvětlení, která je třeba následovat:

// Step 1: start the fetch and obtain a reader
let response = await fetch('https://api.github.com/repos/javascript-tutorial/en.javascript.info/commits?per_page=100');

const reader = response.body.getReader();

// Step 2: get total length
const contentLength = +response.headers.get('Content-Length');

// Step 3: read the data
let receivedLength = 0; // received that many bytes at the moment
let chunks = []; // array of received binary chunks (comprises the body)
while(true) {
  const {done, value} = await reader.read();

  if (done) {
    break;
  }

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

  console.log(`Received ${receivedLength} of ${contentLength}`)
}

// Step 4: concatenate chunks into single 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;
}

// Step 5: decode into a string
let result = new TextDecoder("utf-8").decode(chunksAll);

// We're done!
let commits = JSON.parse(result);
alert(commits[0].author.login);

Pojďme si to vysvětlit krok za krokem:

  1. Provádíme fetch jako obvykle, ale místo volání response.json() , získáme čtečku streamů response.body.getReader() .

    Upozorňujeme, že ke čtení stejné odpovědi nemůžeme použít obě tyto metody:k získání výsledku použijte čtečku nebo metodu odpovědi.

  2. Před čtením můžeme zjistit plnou délku odezvy z Content-Length záhlaví.

    Může chybět u požadavků s křížovým původem (viz kapitola Načítání:Požadavky na křížový původ) a technicky jej server nastavovat nemusí. Ale obvykle je na místě.

  3. Zavolejte na číslo await reader.read() dokud to nebude hotové.

    Shromažďujeme bloky odpovědí v poli chunks . To je důležité, protože po zpracování odpovědi ji nebudeme moci „znovu přečíst“ pomocí response.json() nebo jiným způsobem (můžete to zkusit, dojde k chybě).

  4. Na konci máme chunks – pole Uint8Array bajtové bloky. Musíme je spojit do jediného výsledku. Bohužel neexistuje jediná metoda, která by je spojila, takže existuje nějaký kód, jak to udělat:

    1. Vytváříme chunksAll = new Uint8Array(receivedLength) – pole stejného typu s kombinovanou délkou.
    2. Potom použijte .set(chunk, position) způsob kopírování každého chunk jeden po druhém v něm.
  5. Výsledek máme v chunksAll . Je to však bajtové pole, nikoli řetězec.

    Abychom vytvořili řetězec, musíme tyto bajty interpretovat. Vestavěný TextDecoder dělá přesně to. Pak můžeme JSON.parse v případě potřeby.

    Co když místo řetězce potřebujeme binární obsah? To je ještě jednodušší. Nahraďte kroky 4 a 5 jedním řádkem, který vytvoří Blob ze všech částí:

    let blob = new Blob(chunks);

Na konci máme výsledek (jako řetězec nebo blob, co je vhodné) a sledování průběhu v procesu.

Ještě jednou upozorňujeme, že to není pro nahrávání progress (nyní v žádném případě s fetch ), pouze ke stažení pokrok.

Pokud je velikost neznámá, měli bychom také zkontrolovat receivedLength ve smyčce a zlomit ji, jakmile dosáhne určité hranice. Takže chunks nepřeplní paměť.