概要: このチュートリアルでは、JavaScript の AbortController
を使用して、進行中のネットワークリクエストをキャンセルする方法について説明します。
JavaScript AbortController の概要
AbortController
は、ネットワークリクエストを中止できる Web API です。これは、AbortSignal
をネットワークリクエストに関連付けることで機能します。このシグナルは、必要に応じてネットワークリクエストに中止を伝えるために使用できます。
以下は、AbortController
を使用して fetch リクエストを中止する手順です。
まず、new
キーワードを使用して AbortController
オブジェクトを作成します。
const controller = new AbortController();
Code language: JavaScript (javascript)
次に、AbortController
オブジェクトの signal
プロパティを取得します。signal
プロパティは、AbortSignal
オブジェクトのインスタンスです。
const signal = controller.signal;
Code language: JavaScript (javascript)
この signal
は、fetch リクエストに渡すことができ、その動作を制御できます。
3番目に、AbortSignal
オブジェクトを fetch メソッドに渡します。
fetch(url, { signal });
Code language: JavaScript (javascript)
あるいは、signal
を Request
オブジェクトに渡し、それを fetch
メソッドで使用することもできます。
const request = new Request(url, { signal });
fetch(request);
Code language: JavaScript (javascript)
4番目に、必要に応じて、AbortController
オブジェクトの abort()
メソッドを呼び出して fetch リクエストを中止します。
controller.abort();
Code language: JavaScript (javascript)
たとえば、2秒後に fetch をタイムアウトさせることができます。
setTimeout(() => controller.abort(), 2000);
Code language: JavaScript (javascript)
fetch がすでに完了した後で abort() を呼び出しても問題ありません。fetch はそれを無視します。
5番目に、abort() メソッドを呼び出すと、abort シグナルが通知されます。signal オブジェクトで addEventListener を使用して abort イベントをリッスンできます。
signal.addEventListener('abort', () => {
console.log(signal.aborted); // true
});
Code language: JavaScript (javascript)
最後に、中止された fetch に対応できます。
fetch(url, { signal }).then(response => {
return response.json();
}).then(data => {
console.log(data);
}).catch(err => {
if (err.name === 'AbortError') {
console.log('Fetch aborted');
}
});
Code language: JavaScript (javascript)
AbortError
は実際のエラーではないため、if
ステートメントを使用して AbortError
を特別に処理することに注意してください。
JavaScript AbortController の例
実際には、ユーザーインタラクションを扱うときに AbortController
を使用します。たとえば、検索ページでは、ユーザーが新しい検索語を検索するときに、以前のリクエストを中止できます。
ここでは、Wikipedia で用語を検索するアプリを構築し、最後の検索が完了する前にユーザーが新しい検索を開始した場合、前の検索リクエストをキャンセルします。
ステップ 1. プロジェクトファイルを格納するための新しいディレクトリを作成します。
mkdir wikipedia-search-abortable
Code language: JavaScript (javascript)
ステップ 2. プロジェクトディレクトリ内に次のディレクトリとファイルを作成します。
wikipedia-search-abortable
├── css
| └── style.css
├── img
| ├── spinner.svg
| └── wikipedia-logo.png
├── index.html
└── js
└── app.js
Code language: JavaScript (javascript)
ステップ 3. index.html
ファイルに HTML 構造を作成します。
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Wikipedia Search</title>
<script src="./js/app.js" defer></script>
<link rel="stylesheet" href="./css/style.css">
</head>
<body>
<header>
<img src="./img/wikipedia-logo.png" alt="wikipedia">
<h1>Wikipedia Search</h1>
<form id="search">
<input type="search" name="term" id="term" placeholder="Enter a search term...">
</form>
</header>
<main>
<div id="searchResult"></div>
<div id="loading"></div>
<div id="error"></div>
</main>
</body>
</html>
Code language: HTML, XML (xml)
ステップ 4. 次のコードで app.js
ファイルを修正します。
const form = document.querySelector('#search');
const termInput = document.querySelector('#term');
const error = document.querySelector('#error');
const loading = document.querySelector('#loading');
const resultsContainer = document.querySelector('#searchResult');
let controller = new AbortController();
const search = async (term) => {
// Abort the previous request
console.log('Abort the previous request');
controller.abort();
// Create a new controller for the new request
controller = new AbortController();
const signal = controller.signal;
const url = `https://en.wikipedia.org/w/api.php?action=query&list=search&prop=info|extracts&inprop=url&utf8=&format=json&origin=*&srsearch=${term}`;
const response = await fetch(url, { signal });
const data = await response.json();
return data.query.search;
};
const resetSearchResult = () => {
error.innerHTML = '';
loading.innerHTML = '';
resultsContainer.innerHTML = '';
};
const handleSubmit = async (e) => {
e.preventDefault();
// reset search result
resetSearchResult();
// check if term is empty
const term = termInput.value;
if (term === '') return;
try {
// show the loading
loading.innerHTML = `<img src='./img/spinner.svg' alt='loading...'>`;
// make the search
const results = await search(term);
// show the search result
resultsContainer.innerHTML = renderSearchResult(results);
} catch (err) {
// show the error
error.innerHTML = `Something went wrong.`;
console.error(err);
} finally {
// hide the loading
loading.innerHTML = '';
}
};
const renderSearchResult = (results) => {
return results
.map(({ title, snippet, pageid }) => {
return `<article>
<a href="https://en.wikipedia.org/?curid=${pageid}">
<h2>${title}</h2>
</a>
<div class="summary">${snippet}...</div>
</article>`;
})
.join('');
};
form.addEventListener('submit', handleSubmit);
Code language: JavaScript (javascript)
動作の仕組み。
まず、querySelector
メソッドを使用して DOM 要素を選択します。
const form = document.querySelector('#search');
const termInput = document.querySelector('#term');
const error = document.querySelector('#error');
const loading = document.querySelector('#loading');
const resultsContainer = document.querySelector('#searchResult');
Code language: JavaScript (javascript)
次に、fetch リクエストを中止するための AbortController
を作成します。
let controller = new AbortController();
Code language: JavaScript (javascript)
3番目に、入力検索語に基づいて Wikipedia API を呼び出す検索関数を定義します。
const search = async (term) => {
// Abort the previous request
console.log('Abort the previous request');
controller.abort();
// Create a new controller for the new request
controller = new AbortController();
const signal = controller.signal;
const url = `https://en.wikipedia.org/w/api.php?action=query&list=search&prop=info|extracts&inprop=url&utf8=&format=json&origin=*&srsearch=${term}`;
const response = await fetch(url, { signal });
const data = await response.json();
return data.query.search;
};
Code language: JavaScript (javascript)
search()
関数では、ユーザーが新しい検索を開始した場合、前の検索リクエストをキャンセルするために、AbortController
オブジェクトの abort()
メソッドを呼び出します。
controller.abort();
Code language: JavaScript (javascript)
新しいリクエストを行う前に、新しい AbortController
オブジェクトを作成し、その AbortSignal
を fetch()
メソッドに渡します。
controller = new AbortController();
const signal = controller.signal;
const url = `https://en.wikipedia.org/w/api.php?action=query&list=search&prop=info|extracts&inprop=url&utf8=&format=json&origin=*&srsearch=${term}`;
const response = await fetch(url, { signal });
Code language: JavaScript (javascript)
ステップ 5. エラー、ローディング、および検索結果をリセットする関数を定義します。
const resetSearchResult = () => {
error.innerHTML = '';
loading.innerHTML = '';
resultsContainer.innerHTML = '';
};
Code language: JavaScript (javascript)
ステップ 6. フォームが送信されたときに検索を実行する handleSubmit()
関数を定義します。
const handleSubmit = async (e) => {
e.preventDefault();
// reset search result
resetSearchResult();
// check if term is empty
const term = termInput.value;
if (term === '') return;
try {
// show the loading
loading.innerHTML = `<img src='./img/spinner.svg' alt='loading...'>`;
// make the search
const results = await search(term);
// show the search result
resultsContainer.innerHTML = renderSearchResult(results);
} catch (err) {
// show the error
error.innerHTML = `Something went wrong.`;
console.error(err);
} finally {
// hide the loading
loading.innerHTML = '';
}
};
Code language: JavaScript (javascript)
動作の仕組み。
まず、フォームがサーバーに送信されないようにします。
e.preventDefault();
Code language: JavaScript (javascript)
次に、検索結果をリセットするために resetSearchResult
関数を呼び出します。
resetSearchResult();
Code language: JavaScript (javascript)
3番目に、検索語が空白の場合はすぐに戻ります。
const term = termInput.value;
if (term === '') return;
Code language: JavaScript (javascript)
4番目に、検索リクエストを行う前にローディングプログレスインジケーターを表示します。
loading.innerHTML = `<img src='./img/spinner.svg' alt='loading...'>`;
Code language: JavaScript (javascript)
5番目に、検索リクエストを行います。
const results = await search(term);
Code language: JavaScript (javascript)
6番目に、検索結果を表示します。
resultsContainer.innerHTML = renderSearchResult(results);
Code language: JavaScript (javascript)
7番目に、ユーザーフレンドリーなユーザーメッセージを表示し、コンソールにエラーの詳細を記録します。
} catch (err) {
// show the error
error.innerHTML = `Something went wrong.`;
console.error(err);
}
Code language: JavaScript (javascript)
8番目に、エラーが発生したかどうかに関係なく、ローディングインジケーターを非表示にします。
} finally {
// hide the loading
loading.innerHTML = '';
}
Code language: JavaScript (javascript)
9番目に、検索結果をレンダリングする関数 renderSearchResult
を定義します。
const renderSearchResult = (results) => {
return results
.map(({ title, snippet, pageid }) => {
return `<article>
<a href="https://en.wikipedia.org/?curid=${pageid}">
<h2>${title}</h2>
</a>
<div class="summary">${snippet}...</div>
</article>`;
})
.join('');
};
Code language: JavaScript (javascript)
最後に、フォームの送信イベントハンドラーとして handleSubmit
関数を登録します。
form.addEventListener('submit', handleSubmit);
Code language: JavaScript (javascript)
プロジェクトのソースコードをダウンロードする
まとめ
- 不要なネットワークリクエストを防ぎ、ユーザーインタラクションを効率的に処理するために、Web リクエストを中止するには
AbortController
を使用します。