この記事では、モダンなJavaScriptにおける非同期処理と並列処理の重要な概念と技術について学ぶことができます。具体的には、Fetch APIを用いた非同期通信、Promiseオブジェクトによる非同期処理の制御、そしてWeb Workerを使用したバックグラウンド処理の実装方法を、実際のコード例を通じて理解を深めることができます。これらの技術は、効率的で応答性の高いWebアプリケーション開発の基礎となります。
はじめに
この記事のコードをコピペして出力してみよう。
Fetch APIによる非同期通信
Fetch APIは、ネットワークリクエストを行うための現代的なインターフェースです。
プログラム例:Fetch APIを使用したデータ取得
main.js
document.addEventListener("DOMContentLoaded", () => {
const fetchButton = document.getElementById("fetchData");
const output = document.getElementById("output");
fetchButton.addEventListener("click", fetchData);
function fetchData() {
output.textContent = "データを取得中...";
// JSONPlaceholderの擬似APIからデータを取得
fetch("https://jsonplaceholder.typicode.com/todos/1")
.then((response) => {
// レスポンスが正常かチェック
if (!response.ok) {
throw new Error("Network response was not ok");
}
return response.json();
})
.then((data) => {
// 取得したデータを処理
output.textContent =
"取得したデータ:\n" + JSON.stringify(data, null, 2);
})
.catch((error) => {
// エラーハンドリング
output.textContent = "Fetchエラー: " + error.message;
});
}
});
index.html
<!DOCTYPE html>
<html lang="ja">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>JSONPlaceholder API デモ</title>
<link rel="stylesheet" href="styles.css" />
</head>
<body>
<div class="container">
<h1>JSONPlaceholder API デモ</h1>
<button id="fetchData">データを取得</button>
<div id="output"></div>
</div>
<script src="main.js"></script>
</body>
</html>
styles.css
body {
font-family: Arial, sans-serif;
line-height: 1.6;
margin: 0;
padding: 0;
background-color: #f4f4f4;
}
.container {
width: 80%;
max-width: 600px;
margin: 2rem auto;
padding: 1rem;
background-color: #fff;
border-radius: 5px;
box-shadow: 0 0 10px rgba(0, 0, 0, 0.1);
}
h1 {
color: #333;
text-align: center;
}
button {
display: block;
width: 200px;
margin: 1rem auto;
padding: 0.5rem 1rem;
background-color: #4caf50;
color: white;
border: none;
border-radius: 3px;
cursor: pointer;
font-size: 1rem;
}
button:hover {
background-color: #45a049;
}
#output {
background-color: #eee;
border: 1px solid #ddd;
padding: 1rem;
margin-top: 1rem;
border-radius: 3px;
min-height: 100px;
white-space: pre-wrap;
word-break: break-all;
}
※VSCodeの「Live Server」を使用するか、エクスプローラー内の「index.html」をダブルクリックしてブラウザを立ち上げてください。
このデータはJSONPlaceholderの擬似データです。
出力結果:
このコードは、fetch()
関数を使用してリモートAPIからデータを取得する方法を示しています。then()
メソッドを使用して非同期処理を連鎖させ、catch()
でエラーをハンドリングしています。
このコードは、Fetch APIを使用してウェブサーバーからデータを取得し、それをウェブページに表示する機能を実装しています。
main.jsの解説
イベントリスナーの設定
document.addEventListener("DOMContentLoaded", () => {
// ... (以下の内容)
});
この部分は、DOMの読み込みが完了したときに、以下の関数を実行するよう設定しています。
要素の取得とイベントリスナーの追加
const fetchButton = document.getElementById("fetchData");
const output = document.getElementById("output");
fetchButton.addEventListener("click", fetchData);
これらの行は、HTMLの要素を取得し、”fetchData”ボタンにクリックイベントリスナーを追加しています。
データ取得関数
- 関数の定義:
function fetchData() {
// 関数の内容
}
fetchData
という名前の関数を定義しています。この関数がデータ取得のプロセスを開始します。
- 初期メッセージの表示:
output.textContent = "データを取得中...";
データ取得が始まったことをユーザーに知らせるメッセージを表示します。output
は事前に定義されたHTML要素を参照しています。
- データの取得:
fetch("https://jsonplaceholder.typicode.com/todos/1")
fetch
関数を使用して、指定されたURLからデータを取得し非同期処理を開始します。
- レスポンスの処理:
.then((response) => {
if (!response.ok) {
throw new Error("Network response was not ok");
}
return response.json();
})
then
メソッドを使用して、レスポンスを処理します。response.ok
をチェックして、HTTPレスポンスが正常かどうかを確認します。- 正常でない場合はエラーをスローします。
- 正常な場合、
response.json()
を呼び出してJSONデータを解析します。
- データの表示:
.then((data) => {
output.textContent = "取得したデータ:\n" + JSON.stringify(data, null, 2);
})
- 解析されたJSONデータを受け取り、
JSON.stringify
を使用して整形します。 - 整形されたデータを画面に表示します。
- エラー処理:
.catch((error) => {
output.textContent = "Fetchエラー: " + error.message;
});
catch
メソッドを使用して、処理中に発生したエラーをキャッチします。- エラーメッセージを画面に表示します。
重要なポイント:
- この関数は非同期処理を使用しており、Promiseチェーンを通じてデータ取得とエラー処理を行っています。
fetch
はモダンなJavaScriptでデータを取得するための標準的な方法です。- エラーハンドリングが適切に実装されており、ネットワークエラーやその他の問題に対応できます。
この関数を使用することで、外部APIからデータを取得し、その結果をユーザーに表示することができます。これは、Webアプリケーションでよく使用される基本的なパターンです。
Promiseオブジェクトによる非同期処理
Promiseは、非同期操作の最終的な完了(または失敗)とその結果の値を表現するオブジェクトです。
プログラム例:Promiseを使用した非同期処理
main.js
// 非同期処理を行う関数
function fetchData(url) {
return new Promise((resolve, reject) => {
// 非同期処理(ここではsetTimeoutで模倣)
setTimeout(() => {
if (Math.random() > 0.5) {
resolve("データ取得成功");
} else {
reject("エラー: データ取得失敗");
}
}, 1000);
});
}
document.addEventListener("DOMContentLoaded", () => {
const fetchButton = document.getElementById("fetchButton");
const output = document.getElementById("output");
const status = document.getElementById("status");
fetchButton.addEventListener("click", () => {
output.textContent = "データ取得中...";
status.textContent = "";
// Promiseの使用
fetchData("https://api.example.com/data")
.then((result) => {
output.textContent = result;
output.style.color = "green";
})
.catch((error) => {
output.textContent = error;
output.style.color = "red";
})
.finally(() => {
status.textContent = "処理が完了しました";
});
});
});
index.html
<!DOCTYPE html>
<html lang="ja">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>非同期処理デモ</title>
<link rel="stylesheet" href="styles.css" />
</head>
<body>
<div class="container">
<h1>非同期処理デモ</h1>
<button id="fetchButton">データを取得</button>
<div id="output"></div>
<div id="status"></div>
</div>
<script src="main.js"></script>
</body>
</html>
styles.css
body {
font-family: Arial, sans-serif;
line-height: 1.6;
margin: 0;
padding: 0;
background-color: #f4f4f4;
}
.container {
width: 80%;
max-width: 600px;
margin: 2rem auto;
padding: 1rem;
background-color: #fff;
border-radius: 5px;
box-shadow: 0 0 10px rgba(0, 0, 0, 0.1);
}
h1 {
color: #333;
text-align: center;
}
button {
display: block;
width: 200px;
margin: 1rem auto;
padding: 0.5rem 1rem;
background-color: #4caf50;
color: white;
border: none;
border-radius: 3px;
cursor: pointer;
font-size: 1rem;
}
button:hover {
background-color: #45a049;
}
#output,
#status {
background-color: #eee;
border: 1px solid #ddd;
padding: 1rem;
margin-top: 1rem;
border-radius: 3px;
min-height: 50px;
}
#output {
font-weight: bold;
}
#status {
font-style: italic;
}
※VSCodeの「Live Server」を使用するか、エクスプローラー内の「index.html」をダブルクリックしてブラウザを立ち上げてください。
出力結果:
このコードは、Promise
を使用して非同期処理を扱う方法を示しています。resolve()
と reject()
を使用して成功と失敗を表現し、then()
、catch()
、finally()
メソッドで結果を処理します。
このコードは、非同期処理を模倣し、Promiseを使用してその結果を処理する例を示しています。
main.jsの解説
非同期処理を行う関数
- 関数の定義:
function fetchData(url) {
// 関数の内容
}
fetchData
という名前の関数を定義しています。この関数はurl
パラメータを受け取ります。
- Promiseの作成:
return new Promise((resolve, reject) => {
// Promiseの内容
});
- 関数は新しいPromiseオブジェクトを返します。
Promise
は非同期処理を扱うためのJavaScriptのオブジェクトです。resolve
とreject
は、Promiseの状態を設定するための関数です。
- 非同期処理のシミュレーション:
setTimeout(() => {
// タイムアウト後の処理
}, 1000);
setTimeout
を使用して、1秒(1000ミリ秒)の遅延を作り出しています。- これにより、実際のネットワーク遅延をシミュレートしています。
- ランダムな成功または失敗:
if (Math.random() > 0.5) {
resolve("データ取得成功");
} else {
reject("エラー: データ取得失敗");
}
Math.random()
は0から1の間のランダムな数値を生成します。- 50%の確率で成功(
resolve
)または失敗(reject
)します。 - 成功時は “データ取得成功” というメッセージを返します。
- 失敗時は “エラー: データ取得失敗” というエラーメッセージを返します。
重要なポイント:
- この関数は非同期処理をシミュレートしています。実際のデータ取得は行っていません。
Promise
を使用することで、非同期処理の結果を後で扱うことができます。resolve
とreject
を使って、処理の成功と失敗をシミュレートしています。- ランダムな結果を返すことで、実際のネットワーク通信の不確実性をシミュレートしています。
DOMContentLoadedイベントリスナー
document.addEventListener("DOMContentLoaded", () => {
// ... (以下の内容)
});
この部分は、DOMの読み込みが完了したときに、以下の関数を実行するよう設定しています。
要素の取得とイベントリスナーの追加
const fetchButton = document.getElementById("fetchButton");
const output = document.getElementById("output");
const status = document.getElementById("status");
fetchButton.addEventListener("click", () => {
// ... (以下の内容)
});
これらの行は、HTMLの要素を取得し、”fetchButton”にクリックイベントリスナーを追加しています。
Promiseの使用
fetchData("https://api.example.com/data")
.then((result) => {
output.textContent = result;
output.style.color = "green";
})
.catch((error) => {
output.textContent = error;
output.style.color = "red";
})
.finally(() => {
status.textContent = "処理が完了しました";
});
この部分は:
fetchData
関数を呼び出し、返されたPromiseを処理します。.then()
:Promiseが成功(resolve)した場合の処理を定義します。.catch()
:Promiseが失敗(reject)した場合のエラー処理を定義します。.finally()
:成功・失敗に関わらず、最後に実行される処理を定義します。
重要なポイント
- Promise: 非同期処理の結果を表すオブジェクト。
- resolve/reject: Promiseの状態を成功または失敗に設定する関数。
- .then()/.catch()/.finally(): Promiseの結果を処理するメソッド。
このコードは、Promiseを使用した非同期処理の基本的なパターンを示しています。実際のアプリケーションでは、fetchData
関数は本物のAPIリクエストを行うことになりますが、この例ではsetTimeout
を使用して非同期処理を模倣しています。Promiseを使用することで、非同期コードをより読みやすく、管理しやすい形で書くことができます。
Web Workerによるバックグラウンド処理
Web Workerは、メインスレッドとは別のバックグラウンドスレッドでスクリプトを実行する機能を提供します。
プログラム例:Web Workerの使用
main.js
document.addEventListener("DOMContentLoaded", () => {
const startButton = document.getElementById("startWorker");
const output = document.getElementById("output");
const status = document.getElementById("status");
startButton.addEventListener("click", () => {
output.textContent = "計算中...";
status.textContent = "Worker 実行中";
// Web Workerの作成
const worker = new Worker("worker.js");
// Workerにメッセージを送信
worker.postMessage({ command: "start", number: 10000000 });
// Workerからのメッセージを受信
worker.onmessage = function (event) {
output.textContent = "計算結果: " + event.data;
status.textContent = "計算完了";
};
});
});
worker.js
self.onmessage = function (event) {
if (event.data.command === "start") {
const result = heavyCalculation(event.data.number);
self.postMessage(result);
}
};
function heavyCalculation(n) {
let sum = 0;
for (let i = 0; i < n; i++) {
sum += i;
}
return sum;
}
index.html
<!DOCTYPE html>
<html lang="ja">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Web Worker デモ</title>
<link rel="stylesheet" href="styles.css" />
</head>
<body>
<div class="container">
<h1>Web Worker デモ</h1>
<button id="startWorker">計算開始</button>
<div id="output">結果がここに表示されます</div>
<div id="status"></div>
</div>
<script src="main.js"></script>
</body>
</html>
styles.css
body {
font-family: Arial, sans-serif;
line-height: 1.6;
margin: 0;
padding: 0;
background-color: #f4f4f4;
}
.container {
width: 80%;
max-width: 600px;
margin: 2rem auto;
padding: 1rem;
background-color: #fff;
border-radius: 5px;
box-shadow: 0 0 10px rgba(0, 0, 0, 0.1);
}
h1 {
color: #333;
text-align: center;
}
button {
display: block;
width: 200px;
margin: 1rem auto;
padding: 0.5rem 1rem;
background-color: #4caf50;
color: white;
border: none;
border-radius: 3px;
cursor: pointer;
font-size: 1rem;
}
button:hover {
background-color: #45a049;
}
#output,
#status {
background-color: #eee;
border: 1px solid #ddd;
padding: 1rem;
margin-top: 1rem;
border-radius: 3px;
min-height: 50px;
}
#output {
font-weight: bold;
}
#status {
font-style: italic;
}
※VSCodeの「Live Server」を使用するか、エクスプローラー内の「index.html」をダブルクリックしてブラウザを立ち上げてください。
出力結果:
このコードは、Web Workerを使用して重い計算処理をバックグラウンドで実行する方法を示しています。メインスクリプトで Worker
オブジェクトを作成し、postMessage()
でデータを送信、onmessage
イベントで結果を受信します。
main.jsの解説
DOMContentLoadedイベントリスナー
document.addEventListener("DOMContentLoaded", () => {
// ... (以下の内容)
});
この部分は、DOMの読み込みが完了したときに、以下の関数を実行するよう設定しています。
要素の取得
const startButton = document.getElementById("startWorker");
const output = document.getElementById("output");
const status = document.getElementById("status");
これらの行は、HTMLの要素を取得しています。これらの要素は後でユーザーインターフェースを更新するために使用されます。
ボタンのクリックイベントリスナー
startButton.addEventListener("click", () => {
// ... (以下の内容)
});
“startWorker”ボタンがクリックされたときに実行される関数を設定しています。
Web Workerの作成と使用
output.textContent = "計算中...";
status.textContent = "Worker 実行中";
// Web Workerの作成
const worker = new Worker("worker.js");
// Workerにメッセージを送信
worker.postMessage({ command: "start", number: 10000000 });
// Workerからのメッセージを受信
worker.onmessage = function (event) {
output.textContent = "計算結果: " + event.data;
status.textContent = "計算完了";
};
この部分は:
- ユーザーインターフェースを更新して、計算が開始されたことを示します。
- 新しいWeb Workerを作成します。”worker.js”は別のJavaScriptファイルで、Workerの処理を含みます。
postMessage
を使用してWorkerにデータを送信します。この例では、”start”コマンドと計算に使用する数値を送っています。onmessage
イベントハンドラを設定して、Workerからの結果を受け取ります。結果を受け取ったら、ユーザーインターフェースを更新します。
重要なポイント
- Web Worker: メインスレッドをブロックせずに、バックグラウンドで重い処理を実行できるようにする機能。
- postMessage(): メインスクリプトとWorker間でデータを送受信するためのメソッド。
- onmessage: メッセージを受信したときに実行されるイベントハンドラ。
このコードは、Web Workerを使用して長時間かかる計算処理をバックグラウンドで実行し、その間もウェブページの応答性を維持する方法を示しています。これにより、ユーザー体験を向上させることができます。
Worker.jsの解説
メッセージ受信ハンドラ
self.onmessage = function (event) {
if (event.data.command === "start") {
const result = heavyCalculation(event.data.number);
self.postMessage(result);
}
};
この部分は:
self.onmessage
: Web Worker内でメインスクリプトからのメッセージを受け取るためのイベントハンドラを設定します。event.data
: メインスクリプトから送られてきたデータを含みます。if (event.data.command === "start")
: 受け取ったコマンドが “start” の場合に処理を実行します。heavyCalculation(event.data.number)
: 重い計算を行う関数を呼び出します。self.postMessage(result)
: 計算結果をメインスクリプトに送り返します。
重い計算を行う関数
function heavyCalculation(n) {
let sum = 0;
for (let i = 0; i < n; i++) {
sum += i;
}
return sum;
}
この関数は:
- パラメータ
n
を受け取ります。 - 0から
n-1
までの整数の合計を計算します。 - 計算結果を返します。
重要なポイント
self
: Web Worker内でグローバルスコープを参照するために使用します(ブラウザのウィンドウオブジェクトの代わり)。onmessage
: メインスクリプトからメッセージを受信したときに実行される関数を定義します。postMessage()
: 計算結果をメインスクリプトに送り返すために使用します。heavyCalculation
: この例では単純な合計計算ですが、実際のアプリケーションではより複雑で時間のかかる処理を表します。
このコードは、Web Workerがどのようにメインスクリプトとコミュニケーションを取り、バックグラウンドで重い処理を実行します。heavyCalculation
関数は単純な例ですが、実際のアプリケーションではこれがデータ処理、画像処理、複雑な数学的計算などの時間のかかるタスクに置き換えられます。Web Workerを使用することで、これらの処理をメインのUIスレッドをブロックすることなく実行できます。
まとめ
- Fetch API:モダンな非同期通信の方法、サーバーとのデータのやり取りを効率的に行う方法
- Promiseオブジェクト:非同期処理の流れを制御し、複雑な非同期操作を管理する技術
- Web Worker:メインスレッドをブロックせずに、バックグラウンドで重い処理を実行する方法
これらの技術は、現代のWebアプリケーション開発において不可欠です。Fetch APIを使用することで、従来のXMLHttpRequestよりも簡潔で直感的な非同期通信が可能になります。Promiseオブジェクトは、非同期処理の流れを制御し、コールバック地獄を回避するのに役立ちます。
特に注目すべきはWeb Workerの活用です。これにより、ユーザーインターフェースの応答性を維持しながら、計算量の多い処理をバックグラウンドで実行することが可能になります。
この記事で学んだ知識を活用することで、より高性能で応答性の高いWebアプリケーションを開発するスキルが向上します。非同期処理と並列処理の基本から応用まで理解することは、現代のWeb開発者にとって必須のスキルです。これらの技術を適切に組み合わせることで、ユーザー体験を大幅に向上させ、複雑な処理を効率的に実行するアプリケーションを作成することができます。
コメント