どのようにJavaScriptフェッチの進行状況を監視する-要求とオンデマンドでキャンセルします.
33101 ワード
TL : DR ->コードを私にしてください.https://github.com/tq-bit/fetch-progress
In an earlier post, 私はすでにフェッチを使用してAPIと対話する方法の概要を与えている.この記事では、より詳細な2つのユースケースに詳しく説明します. HTTPリクエストを作成中にダウンロードの進行状況を監視します. ユーザーの入力で要求を優雅に取り消す. 次のようにしたい場合は、このgithubブランチを使用して起動できます.JavaScriptは、いくつかのスタイルとHTMLが含まれます:https://github.com/tq-bit/fetch-progress/tree/get-started .
これは私たちが出発するUIです.進捗インジケータは、フェッチを可視化されます
だからあなたのお気に入りのコードエディタをスピンしてダイビングをしましょう.
先進のものから始める前に、単純な機能を構築しましょう.タスクは、大学を検索することができますユーティリティコードの一部を開発することです.幸運にもHipo ちょうど上に構築するツールがあります. 使っているthis repository 'sは出発地としてAPIをホストしました. ルートURLはhttp://universities.hipolabs.com/ . 私はクエリを使用して米国のすべての大学に私の検索を制限したいと思います. 技術面では、私のフェッチロジックをラッパー関数内に保ちたい. 次のコードを追加することから始めましょう
進捗状況を監視するには、標準の良い部分を再構築する必要があります
次のように追加しましょう
進捗監視のための2つのコア番号がここにあります. The 計算された
既に変数を持っているので
監視機能をラップするには、2つのカスタムイベントを作成します. データチャンクを読み込むたびに、イベント 取得要求が終了したとき、イベント 両方のイベントはウィンドウオブジェクトにバインドされます.このように、彼らは
インサイド
取得する最終的な手順は、カスタムイベントをキャッチし、それに応じて進捗バーの値を変更します.ジャンプしましょう 適切なDOM要素を取得する イベントリスナーを追加する イベントリスナーを追加する 次に、
それでも、いくつかの調整があります. スコープ変数のリセット リクエストをキャンセルする あなたが読書でこれまでに来たならば、2、3のより多くの線のために私と一緒にいてください.
これは音として簡単で、私たちに素敵な、再利用可能な機能を提供します.
次の右側に
作成されたAbortControllerを使用して、我々はすぐに信号を作成することができます.それはコントローラ自体と発信するHTTP要求の間の通信インターフェースとして機能します.内蔵のキルスイッチのように想像してください.
それを設定するには シグナルを作成し、フェッチリクエストオプションに渡します. コントローラの中止関数を呼び出す新しい関数を作成します.
私はVue 3の組成APIを使用して、この機能を再現している.Vueアプリでの監視要求をキャンセルし、要求をキャンセルする場合は、このGISTを見てください.
https://gist.github.com/tq-bit/79d6ab61727ebf29ed0ff9ddc4deedca
残念ながら、私がこの記事のために研究した時間によって、私はアップロード進展をモニターする一般的な方法を見つけることができませんでした.公式WhatWG Githubリポジトリにはopen issue という特徴で
https://github.com/whatwg/fetch/issues/607
In an earlier post, 私はすでにフェッチを使用してAPIと対話する方法の概要を与えている.この記事では、より詳細な2つのユースケースに詳しく説明します.
これは私たちが出発するUIです.進捗インジケータは、フェッチを可視化されます
だからあなたのお気に入りのコードエディタをスピンしてダイビングをしましょう.
基本的なフェッチ要求を作成する
先進のものから始める前に、単純な機能を構築しましょう.タスクは、大学を検索することができますユーティリティコードの一部を開発することです.幸運にもHipo ちょうど上に構築するツールがあります.
client.js
ファイルexport default function http(rootUrl) {
let loading = false;
let chunks = [];
let results = null;
let error = null;
// let controller = null; // We will get to this variable in a second
const json = async (path, options,) => {
loading = true
try {
const response = await fetch(rootUrl + path, { ...options });
if (response.status >= 200 && response.status < 300) {
results = await response.json();
return results
} else {
throw new Error(response.statusText)
}
} catch (err) {
error = err
results = null
return error
} finally {
loading = false
}
}
return { json }
}
次に、この関数をmain.js
ファイルを初期化します.// Import the fetch client and initalize it
import http from './client.js';
const { json } = http('http://universities.hipolabs.com/');
// Grab the DOM elements
const progressbutton = document.getElementById('fetch-button');
// Bind the fetch function to the button's click event
progressbutton.addEventListener('click', async () => {
const universities = await json('search?country=United+States');
console.log(universities);
});
フェッチボタンをクリックすると、要求された大学をコンソールに出力します.再構築します。JSON ()メソッド
進捗状況を監視するには、標準の良い部分を再構築する必要があります
.json()
メソッド.また、我々はまた、チャンクによってチャンク応答体を組み立てるの世話をしなければならないことを暗示する.I've written an article about handling Node.js streams earlier. The approach shown here is quite similar.
次のように追加しましょう
client.js
ファイルの下にjson
機能export default function http(rootUrl) {
// ... previous functions
const _readBody = async (response) => {
const reader = response.body.getReader();
// Declare received as 0 initially
let received = 0;
// Loop through the response stream and extract data chunks
while (loading) {
const { done, value } = await reader.read();
if (done) {
// Finish loading
loading = false;
} else {
// Push values to the chunk array
chunks.push(value);
}
}
// Concat the chinks into a single array
let body = new Uint8Array(received);
let position = 0;
// Order the chunks by their respective position
for (let chunk of chunks) {
body.set(chunk, position);
position += chunk.length;
}
// Decode the response and return it
return new TextDecoder('utf-8').decode(body);
}
return { json }
}
次は入れ替えましょうresponse.json()
次のようになります. // results = response.json();
// return results;
results = await _readBody(response)
return JSON.parse(results)
ブラウザでの応答は、以前にデコードされたJSONオブジェクトと同じです.レスポンスの本体自体がreadable stream , 新しいデータが読み込まれているか、ストリームが閉じられているかを監視することができます.最大と現在のデータ長を取得する
進捗監視のための2つのコア番号がここにあります.
content-length
レスポンスからのヘッダ、変数length
. length
受信データの塊、変数received
. Note that this function does not work if the
content-length
header is not configured on the serverside.
既に変数を持っているので
received
ご利用いただけますcontent-length
我々に_readBody
機能 const _readBody = async (response) => {
const reader = response.body.getReader();
// This header must be configured serverside
const length = +response.headers.get('content-length');
// Declare received as 0 initially
let received = 0;
// ...
if (done) {
// Finish loading
loading = false;
} else {
// Push values to the chunk array
chunks.push(value);
// Add on to the received length
received += value.length;
}
}
それで、我々はすべての関連指標値をご利用いただけます.不足しているのは、呼び出し元の関数にそれらを放出する方法です.これは簡単にJavaScriptフレームワークの反応機能を使用して、反応フックやVueの組成APIのように行うことができます.この場合、しかし、我々は組み込みブラウザ機能CustomEvent
.イベントで取得可能なフェッチプログレスを作る
監視機能をラップするには、2つのカスタムイベントを作成します.
fetch-progress
. fetch-finished
. http
- 関数のスコープ.インサイド
_readBody()
, 調整.次のようにループします. const _readBody = async (response) => {
// ...
// Loop through the response stream and extract data chunks
while (loading) {
const { done, value } = await reader.read();
const payload = { detail: { received, length, loading } }
const onProgress = new CustomEvent('fetch-progress', payload);
const onFinished = new CustomEvent('fetch-finished', payload)
if (done) {
// Finish loading
loading = false;
// Fired when reading the response body finishes
window.dispatchEvent(onFinished)
} else {
// Push values to the chunk array
chunks.push(value);
received += value.length;
// Fired on each .read() - progress tick
window.dispatchEvent(onProgress);
}
}
// ...
}
ディスプレイの進行状況
取得する最終的な手順は、カスタムイベントをキャッチし、それに応じて進捗バーの値を変更します.ジャンプしましょう
main.js
ファイルを次のように調整します.fetch-progress
fetch-finished
e.detail
プロパティとプログレスバー値を調整します.// Import the fetch client and initalize it
import http from './client.js';
// Grab the DOM elements
const progressbar = document.getElementById('progress-bar');
const progressbutton = document.getElementById('fetch-button');
const progresslabel = document.getElementById('progress-label');
const { json } = http('http://universities.hipolabs.com/');
const setProgressbarValue = (payload) => {
const { received, length, loading } = payload;
const value = ((received / length) * 100).toFixed(2);
progresslabel.textContent = `Download progress: ${value}%`;
progressbar.value = value;
};
// Bind the fetch function to the button's click event
progressbutton.addEventListener('click', async () => {
const universities = await json('search?country=United+States');
console.log(universities);
});
window.addEventListener('fetch-progress', (e) => {
setProgressbarValue(e.detail);
});
window.addEventListener('fetch-finished', (e) => {
setProgressbarValue(e.detail);
});
そして、我々はそれを持っている-あなたは今あなたのフェッチ要求の進行状況を監視することができます.それでも、いくつかの調整があります.
スコープ変数のリセット
これは音として簡単で、私たちに素敵な、再利用可能な機能を提供します.
次の右側に
_readBody()
- あなたの関数client.js
ファイルconst _resetLocals = () => {
loading = false;
chunks = [];
results = null;
error = null;
controller = new AbortController();
}
Remeber that you must call
resetLocals()
in thejson()
function first.
export default function http(rootUrl) {
let loading = false;
let chunks = [];
let results = null;
let error = null;
let controller = null; // Make sure to uncomment this variable
const json = async (path, options,) => {
_resetLocals();
loading = true
// ... rest of the json function
}
// ... rest of the http function
上記の機能で、私たちはまた、新しいオブジェクトAbortController
. 名前が示すように、アクティブリクエストをカットするために使用できます.進行中の要求を取り消す
作成されたAbortControllerを使用して、我々はすぐに信号を作成することができます.それはコントローラ自体と発信するHTTP要求の間の通信インターフェースとして機能します.内蔵のキルスイッチのように想像してください.
それを設定するには
client.js
ファイル:const json = async (path, options,) => {
_resetLocals();
let signal = controller.signal;
loading = true
try {
const response = await fetch(rootUrl + path, { signal, ...options });
// ... rest of the trycatch function
}
// ... rest of the json function
}
// Cancel an ongoing fetch request
const cancel = () => {
_resetLocals();
controller.abort();
};
// Make sure to export cancel
return { json, cancel }
最後にジャンプしましょうmain.js
イベントを2番目のボタンにバインドする// ... other variable declarations
const abortbutton = document.getElementById('abort-button');
const { json, cancel } = http('http://universities.hipolabs.com/');
// ... other functions and event listeners
abortbutton.addEventListener('click', () => {
cancel()
alert('Request has been cancelled')
})
あなたがすぐにFETCHとCancel要求を直後にヒットするならば、あなたがそれが200のHTTPステータスを返すとしても、要求がデータを全く返さないという警告を見るでしょう.UPDATE : VUE 3フェッチ機能
私はVue 3の組成APIを使用して、この機能を再現している.Vueアプリでの監視要求をキャンセルし、要求をキャンセルする場合は、このGISTを見てください.
https://gist.github.com/tq-bit/79d6ab61727ebf29ed0ff9ddc4deedca
次は?
残念ながら、私がこの記事のために研究した時間によって、私はアップロード進展をモニターする一般的な方法を見つけることができませんでした.公式WhatWG Githubリポジトリにはopen issue という特徴で
FetchObserver
. しかし、我々はそれが実装されるために我慢しなければならないようです.おそらく、この記事に記載されている機能も簡単になります.未来は語るだろう.https://github.com/whatwg/fetch/issues/607
This post was originally published at https://blog.q-bit.me/monitoring-and-canceling-a-javascript-fetch-request/
Thank you for reading. If you enjoyed this article, let's stay in touch on Twitter 🐤
Reference
この問題について(どのようにJavaScriptフェッチの進行状況を監視する-要求とオンデマンドでキャンセルします.), 我々は、より多くの情報をここで見つけました https://dev.to/tqbit/how-to-monitor-the-progress-of-a-javascript-fetch-request-and-cancel-it-on-demand-107fテキストは自由に共有またはコピーできます。ただし、このドキュメントのURLは参考URLとして残しておいてください。
Collection and Share based on the CC Protocol