Contents
async/awaitの導入と業務ニーズへの対応
JavaScriptでは、WebAPIとの連携や大量データ処理などで非同期処理が必要不可欠です。しかし、従来のコールバック関数を使う方法ではコードが複雑になりやすく、可読性や保守性に課題がありました。この記事では、async/awaitを活用した非同期処理の実装法を具体的に解説し、WebAPI連携や並列処理最適化といった業務ニーズに対応するコード構成をお伝えします。
非同期処理がなぜ必要か
JavaScriptはシングルスレッドで動作するため、長時間の処理(例:ファイル読み込み、API通信)を同期的に実行するとUIがフリーズしてしまう可能性があります。非同期処理を使うことで、処理中のUI操作に支障が出ることなく、ネットワークリクエストやデータ取得を実行できます。
WebAPI連携における課題
WebAPIとの通信では、通常「レスポンスが返ってくるまで待つ」という同期的な処理は避けられませんが、async/awaitを使うことでその待ち時間を別の処理と並列化可能です。たとえば、複数の外部APIを同時に呼び出す場合、パフォーマンスの改善やコードの見通し向上に大きく貢献します。
async/awaitの基本構文と動作原理
async/awaitはPromiseオブジェクトとの連携で動作し、非同期処理を「同期的な流れ」のように記述できる仕組みです。これにより、コールバック地獄や複雑なPromiseチェインの必要性が大幅に減ります。
async関数の定義
async関数は、必ずPromiseオブジェクトを返す関数になります。以下に基本的な構文と動作例を示します:
|
1 2 3 4 5 |
async function fetchData() { const response = await fetch('https://api.example.com/data'); return await response.json(); } |
この例ではfetch()で非同期通信を行い、awaitによってその結果を待ちます。awaitはPromiseが解決(resolve)されるまで処理を停止し、解決後の値を返します。
awaitキーワードの役割
awaitは、Promiseオブジェクトに対して「待つ」という動作を行います。同期的なコードと同様に書けるため、処理の流れが直感的になります。
非同期処理でのエラーハンドリング技法
非同期関数内で発生したエラーは、通常のtry/catch文や.catch()メソッドで捕捉可能です。これにより、ネットワーク失敗やAPI側のエラーを安定して処理できます。
try/catch文による例外捕獲
以下のようにasync関数内にtry/catchを配置することで、非同期処理中のエラーをキャッチできます:
|
1 2 3 4 5 6 7 8 9 10 11 |
async function safeFetch() { try { const response = await fetch('https://api.example.com/data'); if (!response.ok) throw new Error("ネットワーク応答が異常"); return await response.json(); } catch (error) { console.error('エラー発生:', error.message); return null; } } |
このコードでは、fetch()が失敗したり、レスポンスコードが200番台でなかったりした場合にエラーメッセージを出力し、nullを返却します。
.catch()の活用法
Promiseチェインのように.catch()メソッドを使えば、複数の非同期処理を一括してエラーハンドリングできます:
|
1 2 3 4 5 6 7 8 9 |
fetch('https://api.example.com/data') .then(response => { if (!response.ok) throw new Error("ネットワーク応答が異常"); return response.json(); }) .catch(error => { console.error('エラー発生:', error.message); }); |
fetch APIによるリアルタイムデータ取得例
天気APIの非同期呼び出し
以下は、OpenWeatherMapなどの天気APIを用いた非同期通信コードです。
|
1 2 3 4 5 6 7 8 9 10 11 |
async function getWeather(city) { try { const response = await fetch(`https://api.example.com/weather?q=${city}`); if (!response.ok) throw new Error("天気情報取得失敗"); return await response.json(); } catch (error) { console.error('エラー発生:', error.message); return null; } } |
この関数は、指定された都市名をパラメータとしてAPIに送信し、JSON形式の天気データを返却します。
JSONデータのパース処理
response.json()メソッドは、レスポンスボディをJSONオブジェクトに変換します。以下のコードでは取得したデータから温度と湿度だけを取り出しています:
|
1 2 3 4 5 6 7 |
async function displayWeather(city) { const data = await getWeather(city); if (data && data.temperature !== undefined && data.humidity !== undefined) { console.log(`気温: ${data.temperature}℃, 湿度: ${data.humidity}%`); } } |
複数API同時呼び出しへの最適化手法
Promise.allによる並列処理
複数の非同期処理を同時に実行するにはPromise.all()を使用します。以下は天気情報とニュース情報を同時に取得する例です:
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 |
async function getMultipleData() { try { const [weather, news] = await Promise.all([ fetch('https://api.example.com/weather'), fetch('https://api.example.com/news') ]); return { weather: await weather.json(), news: await news.json() }; } catch (error) { console.error('エラー発生:', error.message); throw error; } } |
このコードでは、ネットワーク失敗が発生した場合に個別に処理を分岐できます。
パフォーマンス改善効果
並列処理のメリットは、処理時間が短縮されることです。以下に並列/直列処理での処理時間を比較します:
| 処理方法 | タスク数 | 実行時間(ms) |
|---|---|---|
| 直列処理 | 3 | 120 |
| 並列処理 | 3 | 40 |
コールバック地獄からの脱却とasync/awaitの威力
ネストされたコールバックの問題点
以下は、従来のコールバック関数による非同期処理です。このように深いネストが発生するとコードの可読性が著しく低下します:
|
1 2 3 4 5 6 7 8 |
fetch('https://api.example.com/data1', (err, res) => { if (err) return console.error(err); fetch('https://api.example.com/data2', (err, res) => { if (err) return console.error(err); console.log(res); }); }); |
非同期コードの洗練された書き方
async/awaitを使うことで、ネストを解消し、直感的な処理フローにできます:
|
1 2 3 4 5 6 |
async function getData() { const data1 = await fetch('https://api.example.com/data1'); const data2 = await fetch('https://api.example.com/data2'); console.log(data1, data2); } |
このように、「待つ」処理を直列に書けるため、メンテナンス性が向上します。
実践コードのコピー&ペースト可能エリア
以下のコードは、即座に実装可能なasync/await構文です。Ctrl+C & Ctrl+Vで簡単に利用できます:
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 |
// 非同期処理の実装例(fetch API) async function getAsyncData() { try { const controller = new AbortController(); const timeoutId = setTimeout(() => controller.abort(), 5000); const response = await fetch('https://api.example.com/data', { method: 'GET', headers: { 'Content-Type': 'application/json' }, signal: controller.signal }); if (!response.ok) throw new Error("通信失敗"); const data = await response.json(); clearTimeout(timeoutId); return data; } catch (error) { console.error(error); return null; } } // 実行 getAsyncData().then(data => { console.log("取得データ:", data); }); |
CORS対策とタイムアウト設定の重要性
CORS設定とセキュリティ
CORS(クロスオリジンリソース共有)は、異なるドメイン間でのAPIアクセスを制御する仕組みです。サーバー側でAccess-Control-Allow-Originヘッダーを設定することで、クライアントサイドでリクエストが許可されます。
|
1 2 3 4 5 |
fetch('https://api.example.com/data', { mode: 'cors', headers: { 'Content-Type': 'application/json' } }); |
タイムアウト処理の実装方法
ネットワーク遅延やサーバー障害に対応するため、タイムアウトを設定します。AbortControllerを使ってリクエストを中止できます:
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 |
async function fetchDataWithTimeout() { const controller = new AbortController(); const timeoutId = setTimeout(() => controller.abort(), 5000); try { const response = await fetch('https://api.example.com/data', { signal: controller.signal }); return await response.json(); } catch (error) { console.error("タイムアウトまたはエラー:", error.message); throw error; } finally { clearTimeout(timeoutId); } } |
結論
async/awaitは非同期処理を直感的かつ読みやすいコードで実装できる仕組みであり、開発効率の向上に大きく貢献します。ただし、JSONパースやネットワーク通信におけるエラーハンドリング、CORS対策、タイムアウト設定などは、安定したアプリケーション構築には不可欠です。本記事で解説した技術を活用し、実際のプロジェクトに応用してください。