ÐеÑод fetch позволÑÐµÑ Ð¾ÑÑлеживаÑÑ Ð¿ÑоÑеÑÑ Ð¿Ð¾Ð»ÑÑÐµÐ½Ð¸Ñ Ð´Ð°Ð½Ð½ÑÑ
.
ÐамеÑим, на даннÑй Ð¼Ð¾Ð¼ÐµÐ½Ñ Ð² fetch Ð½ÐµÑ ÑпоÑоба оÑÑлеживаÑÑ Ð¿ÑоÑеÑÑ Ð¾ÑпÑавки. ÐÐ»Ñ ÑÑого иÑполÑзÑйÑе XMLHttpRequest, позже Ð¼Ñ ÐµÐ³Ð¾ ÑаÑÑмоÑÑим.
ЧÑÐ¾Ð±Ñ Ð¾ÑÑлеживаÑÑ Ñ
од загÑÑзки даннÑÑ
Ñ ÑеÑвеÑа, можно иÑполÑзоваÑÑ ÑвойÑÑво response.body. ÐÑо ReadableStream («поÑок Ð´Ð»Ñ ÑÑениÑ») â оÑобÑй обÑекÑ, коÑоÑÑй пÑедоÑÑавлÑÐµÑ Ñело оÑвеÑа по ÑаÑÑÑм, по меÑе поÑÑÑплениÑ. ÐоÑоки Ð´Ð»Ñ ÑÑÐµÐ½Ð¸Ñ Ð¾Ð¿Ð¸ÑÐ°Ð½Ñ Ð² ÑпеÑиÑикаÑии Streams API.
РоÑлиÑие Ð¾Ñ response.text(), response.json() и дÑÑгиÑ
меÑодов, response.body даÑÑ Ð¿Ð¾Ð»Ð½Ñй конÑÑÐ¾Ð»Ñ Ð½Ð°Ð´ пÑоÑеÑÑом ÑÑениÑ, и Ð¼Ñ Ð¼Ð¾Ð¶ÐµÐ¼ подÑÑиÑаÑÑ, ÑколÑко даннÑÑ
полÑÑено на каждÑй моменÑ.
ÐÐ¾Ñ Ð¿ÑимеÑнÑй код, коÑоÑÑй ÑиÑÐ°ÐµÑ Ð¾ÑÐ²ÐµÑ Ð¸Ð· response.body:
// вмеÑÑо response.json() и дÑÑгиÑ
меÑодов
const reader = response.body.getReader();
// беÑконеÑнÑй Ñикл, пока идÑÑ Ð·Ð°Ð³ÑÑзка
while(true) {
// done ÑÑановиÑÑÑ true в поÑледнем ÑÑагменÑе
// value - Uint8Array из байÑов каждого ÑÑагменÑа
const {done, value} = await reader.read();
if (done) {
break;
}
console.log(`ÐолÑÑено ${value.length} байÑ`)
}
РезÑлÑÑÐ°Ñ Ð²Ñзова await reader.read() â ÑÑо обÑÐµÐºÑ Ñ Ð´Ð²ÑÐ¼Ñ ÑвойÑÑвами:
doneâtrue, когда ÑÑение законÑено, инаÑеfalse.valueâ ÑипизиÑованнÑй маÑÑив даннÑÑ Ð¾ÑвеÑаUint8Array.
Streams API Ñакже опиÑÑÐ²Ð°ÐµÑ Ð°ÑинÑ
ÑоннÑй пеÑÐµÐ±Ð¾Ñ Ð¿Ð¾ ReadableStream, пÑи помоÑи Ñикла for await..of, но он пока Ñлабо поддеÑживаеÑÑÑ (Ñм. задаÑи Ð´Ð»Ñ Ð±ÑаÑзеÑов), поÑÑÐ¾Ð¼Ñ Ð¸ÑполÑзÑем Ñикл while.
ÐÑ Ð¿Ð¾Ð»ÑÑаем новÑе ÑÑагменÑÑ Ð´Ð°Ð½Ð½ÑÑ
в Ñикле, пока загÑÑзка не завеÑÑиÑÑÑ, Ñо еÑÑÑ Ð¿Ð¾ÐºÐ° done не ÑÑÐ°Ð½ÐµÑ true.
ЧÑÐ¾Ð±Ñ Ð¾ÑÑлеживаÑÑ Ð¿ÑоÑеÑÑ Ð·Ð°Ð³ÑÑзки, нам нÑжно пÑи полÑÑении оÑеÑедного ÑÑагменÑа пÑибавлÑÑÑ ÐµÐ³Ð¾ Ð´Ð»Ð¸Ð½Ñ value к ÑÑÑÑÑикÑ.
ÐÐ¾Ñ Ð¿Ð¾Ð»Ð½Ñй ÑабоÑий пÑимеÑ, коÑоÑÑй полÑÑÐ°ÐµÑ Ð¾ÑÐ²ÐµÑ ÑеÑвеÑа и в пÑоÑеÑÑе полÑÑÐµÐ½Ð¸Ñ Ð²ÑÐ²Ð¾Ð´Ð¸Ñ Ð² конÑоли Ð´Ð»Ð¸Ð½Ñ Ð¿Ð¾Ð»ÑÑеннÑÑ Ð´Ð°Ð½Ð½ÑÑ :
// Шаг 1: наÑинаем загÑÑÐ·ÐºÑ fetch, полÑÑаем поÑок Ð´Ð»Ñ ÑÑениÑ
let response = await fetch('https://api.github.com/repos/javascript-tutorial/en.javascript.info/commits?per_page=100');
const reader = response.body.getReader();
// Шаг 2: полÑÑаем Ð´Ð»Ð¸Ð½Ñ ÑодеÑжимого оÑвеÑа
const contentLength = +response.headers.get('Content-Length');
// Шаг 3: ÑÑиÑÑваем даннÑе:
let receivedLength = 0; // колиÑеÑÑво байÑ, полÑÑеннÑÑ
на даннÑй моменÑ
let chunks = []; // маÑÑив полÑÑеннÑÑ
двоиÑнÑÑ
ÑÑагменÑов (ÑоÑÑавлÑÑÑиÑ
Ñело оÑвеÑа)
while(true) {
const {done, value} = await reader.read();
if (done) {
break;
}
chunks.push(value);
receivedLength += value.length;
console.log(`ÐолÑÑено ${receivedLength} из ${contentLength}`)
}
// Шаг 4: Ñоединим ÑÑагменÑÑ Ð² обÑий ÑипизиÑованнÑй маÑÑив 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;
}
// Шаг 5: декодиÑÑем Uint8Array обÑаÑно в ÑÑÑокÑ
let result = new TextDecoder("utf-8").decode(chunksAll);
// ÐоÑово!
let commits = JSON.parse(result);
alert(commits[0].author.login);
РазбеÑÑмÑÑ, ÑÑо здеÑÑ Ð¿ÑоизоÑло:
-
ÐÑ Ð¾Ð±ÑаÑаемÑÑ Ðº
fetchкак обÑÑно, но вмеÑÑо вÑзоваresponse.json()Ð¼Ñ Ð¿Ð¾Ð»ÑÑаем доÑÑÑп к поÑÐ¾ÐºÑ ÑÑениÑresponse.body.getReader().ÐбÑаÑиÑе внимание, ÑÑо Ð¼Ñ Ð½Ðµ можем иÑполÑзоваÑÑ Ð¾Ð´Ð½Ð¾Ð²Ñеменно оба ÑÑи меÑода Ð´Ð»Ñ ÑÑÐµÐ½Ð¸Ñ Ð¾Ð´Ð½Ð¾Ð³Ð¾ и Ñого же оÑвеÑа: либо обÑÑнÑй меÑод
response.json(), либо ÑÑение поÑокаresponse.body. -
ÐÑÑ Ð´Ð¾ ÑÑÐµÐ½Ð¸Ñ Ð¿Ð¾Ñока Ð¼Ñ Ð¼Ð¾Ð¶ÐµÐ¼ вÑÑиÑлиÑÑ Ð¿Ð¾Ð»Ð½ÑÑ Ð´Ð»Ð¸Ð½Ñ Ð¾ÑвеÑа из заголовка
Content-Length.Ðн Ð¼Ð¾Ð¶ÐµÑ Ð±ÑÑÑ Ð½ÐµÑиÑаемÑм пÑи запÑоÑÐ°Ñ Ð½Ð° дÑÑгой иÑÑоÑник (подÑобнее в Ñазделе Fetch: запÑоÑÑ Ð½Ð° дÑÑгие ÑайÑÑ) и, в обÑем-Ñо, ÑеÑвеÑÑ Ð½ÐµÐ¾Ð±ÑзаÑелÑно его ÑÑÑанавливаÑÑ. Тем не менее, обÑÑно длина Ñказана.
-
ÐÑзÑваем
await reader.read()до оконÑÐ°Ð½Ð¸Ñ Ð·Ð°Ð³ÑÑзки.ÐÑÑ, ÑÑо полÑÑили, Ð¼Ñ ÑкладÑваем по «кÑÑоÑкам» в маÑÑив
chunks. ÐÑо важно, поÑÐ¾Ð¼Ñ ÑÑо поÑле Ñого, как оÑÐ²ÐµÑ Ð¿Ð¾Ð»ÑÑен, Ð¼Ñ Ñже не Ñможем «пеÑеÑиÑаÑÑ» его, иÑполÑзÑÑresponse.json()или лÑбой дÑÑгой ÑпоÑоб (попÑобÑйÑе â бÑÐ´ÐµÑ Ð¾Ñибка). -
Ð Ñамом конÑе Ñ Ð½Ð°Ñ ÑипизиÑованнÑй маÑÑив â
Uint8Array. РнÑм Ð½Ð°Ñ Ð¾Ð´ÑÑÑÑ ÑÑагменÑÑ Ð´Ð°Ð½Ð½ÑÑ . Ðам нÑжно Ð¸Ñ ÑклеиÑÑ, ÑÑÐ¾Ð±Ñ Ð¿Ð¾Ð»ÑÑиÑÑ ÑÑÑокÑ. Ð ÑожалениÑ, Ð´Ð»Ñ ÑÑого Ð½ÐµÑ ÑпеÑиалÑного меÑода, но можно ÑделаÑÑ, напÑимеÑ, Ñак:- СоздаÑм
chunksAll = new Uint8Array(receivedLength)â маÑÑив Ñого же Ñипа заданной длинÑ. - ÐÑполÑзÑем
.set(chunk, position)Ð´Ð»Ñ ÐºÐ¾Ð¿Ð¸ÑÐ¾Ð²Ð°Ð½Ð¸Ñ ÐºÐ°Ð¶Ð´Ð¾Ð³Ð¾ ÑÑагменÑа дÑÑг за дÑÑгом в него.
- СоздаÑм
-
ÐÐ°Ñ ÑезÑлÑÑÐ°Ñ ÑепеÑÑ Ñ ÑаниÑÑÑ Ð²
chunksAll. ÐÑо не ÑÑÑока, а байÑовÑй маÑÑив.ЧÑÐ¾Ð±Ñ Ð¿Ð¾Ð»ÑÑиÑÑ Ð¸Ð¼ÐµÐ½Ð½Ð¾ ÑÑÑокÑ, надо декодиÑоваÑÑ Ð±Ð°Ð¹ÑÑ. ÐÑÑÑоеннÑй обÑÐµÐºÑ TextDecoder как Ñаз ÑÑим и занимаеÑÑÑ. ÐоÑом Ð¼Ñ Ð¼Ð¾Ð¶ÐµÐ¼, еÑли Ð½ÐµÐ¾Ð±Ñ Ð¾Ð´Ð¸Ð¼Ð¾, пÑеобÑазоваÑÑ ÑÑÑÐ¾ÐºÑ Ð² даннÑе Ñ Ð¿Ð¾Ð¼Ð¾ÑÑÑ
JSON.parse.ЧÑо еÑли ÑезÑлÑÑÐ°Ñ Ð½Ð°Ð¼ нÑжен в бинаÑном виде вмеÑÑо ÑÑÑоки? ÐÑо еÑÑ Ð¿ÑоÑе. ÐамениÑе Ñаги 4 и 5 на Ñоздание единого
Blobиз вÑÐµÑ ÑÑагменÑов:let blob = new Blob(chunks);
РиÑоге Ñ Ð½Ð°Ñ ÐµÑÑÑ ÑезÑлÑÑÐ°Ñ (ÑÑÑоки или Blob, ÑмоÑÑÑ ÑÑо Ñдобно) и оÑÑлеживание пÑогÑеÑÑа полÑÑениÑ.
Ðа вÑÑкий ÑлÑÑай повÑоÑимÑÑ, ÑÑо здеÑÑ Ð¼Ñ ÑаÑÑмоÑÑели, как оÑÑлеживаÑÑ Ð¿ÑоÑеÑÑ Ð¿Ð¾Ð»ÑÑÐµÐ½Ð¸Ñ Ð´Ð°Ð½Ð½ÑÑ
Ñ ÑеÑвеÑа, а не иÑ
оÑпÑавки на ÑеÑвеÑ. ÐÐ»Ñ Ð¾ÑÑÐ»ÐµÐ¶Ð¸Ð²Ð°Ð½Ð¸Ñ Ð¾ÑпÑавки Ñ fetch пока Ð½ÐµÑ ÑпоÑоба.
ÐомменÑаÑии
<code>, Ð´Ð»Ñ Ð½ÐµÑколÑÐºÐ¸Ñ ÑÑÑок кода — Ñег<pre>, еÑли болÑÑе 10 ÑÑÑок — ÑÑÑÐ»ÐºÑ Ð½Ð° пеÑоÑниÑÑ (plnkr, JSBin, codepenâ¦)