Fetch APIとAJAXについて

JavaScriptを使ってウェブページからデータを取得したり、
サーバーにデータを送信したりする際には、非同期通信が重要です。
非同期通信を行うことで、ページをリロードすることなくサーバーとやり取りができ、
ユーザー体験を向上させることができます。
非同期通信の実装には、従来から使われてきたAJAX(Asynchronous JavaScript and XML)と、
近年導入されたFetch APIの2つの主要な方法があります。
本記事では、AJAXとFetch APIの違い、それぞれの使い方、
そして現代のウェブ開発における利点と欠点について詳しく説明します。

非同期通信とAJAXの基本概念

非同期通信とは

非同期通信とは、サーバーからデータを取得したり、サーバーにデータを送信する際に、
ユーザーインターフェースがフリーズすることなく処理を続行できる通信方法です。
これにより、ユーザーがページを操作している間にも、
バックグラウンドでデータのやり取りが行われます。

AJAXの基本

AJAX(Asynchronous JavaScript and XML)は、JavaScriptを使用して非同期通信を行うための技術です。
AJAXの中心となるのはXMLHttpRequestオブジェクトで、これを使ってサーバーにリクエストを送信し、
レスポンスを受け取ることができます。
AJAXは、ページ全体をリロードすることなく、ページの一部を更新したり、
データを表示したりするのに広く使われてきました。

XMLHttpRequestによるAJAXの実装

基本的な使い方

XMLHttpRequestを使用してサーバーからデータを取得する基本的な方法を以下に示します。

const xhr = new XMLHttpRequest();
xhr.open("GET", "https://jsonplaceholder.typicode.com/posts", true);

xhr.onreadystatechange = function () {
if (xhr.readyState === 4 && xhr.status === 200) {
console.log(JSON.parse(xhr.responseText));
}
};

xhr.send();

この例では、XMLHttpRequestオブジェクトxhrを作成し、
openメソッドを使ってGETリクエストを設定します。
onreadystatechangeイベントハンドラーは、リクエストの状態が変わるたびに呼び出され、
readyState4(完了)かつstatus200(成功)の場合に、
サーバーからのレスポンスが処理されます。

POSTリクエストの送信

POSTリクエストを送信してサーバーにデータを送信する例です。

const xhr = new XMLHttpRequest();
xhr.open("POST", "https://jsonplaceholder.typicode.com/posts", true);
xhr.setRequestHeader("Content-Type", "application/json;charset=UTF-8");

xhr.onreadystatechange = function () {
if (xhr.readyState === 4 && xhr.status === 201) {
console.log(JSON.parse(xhr.responseText));
}
};

const data = JSON.stringify({ title: "foo", body: "bar", userId: 1 });
xhr.send(data);

この例では、setRequestHeaderメソッドを使ってリクエストヘッダーを設定し、
POSTリクエストとしてJSONデータを送信しています。
レスポンスが成功した場合(HTTPステータス201)、その結果をコンソールに出力します。

AJAXの限界と問題点

AJAXは長い間、非同期通信の標準的な手段として使われてきましたが、いくつかの問題点もあります。

複雑なAPI

XMLHttpRequestは、そのAPIがやや複雑で、非同期通信の実装が冗長になりがちです。
特に、エラーハンドリングやタイムアウトの設定など、基本的なタスクでも多くのコードが必要になります。

コードの読みづらさ

XMLHttpRequestを使ったコードは、コールバック関数が多用され、読みづらくなりがちです。

機能の限定

XMLHttpRequestは、特定のタスクに特化したAPIであり、
モダンなJavaScriptの機能や簡潔な記法に対応していません。

Fetch APIによるモダンな非同期通信

Fetch APIの概要

Fetch APIは、AJAXに代わるモダンな非同期通信手段として、ES6で導入されました。
Fetch APIは、非同期通信をよりシンプルかつ直感的に行うためのAPIを提供します。
Fetch APIは、Promiseベースで動作し、非同期操作の結果をPromiseオブジェクトとして返します。
これにより、非同期操作のチェーン処理やエラーハンドリングが容易になり、
コードが簡潔で読みやすくなります。

Fetch APIの基本的な使い方

Fetch APIを使ってGETリクエストを送信し、データを取得する基本的な例を示します。

fetch("https://jsonplaceholder.typicode.com/posts")
.then((response) => {
if (!response.ok) {
throw new Error("Network response was not ok");
}
return response.json();
})
.then((data) => {
console.log(data);
})
.catch((error) => {
console.error("There was a problem with the fetch operation:", error);
});

この例では、fetch関数にURLを渡してGETリクエストを送信し、
thenメソッドを使ってレスポンスを処理しています。
レスポンスが成功した場合はresponse.json()でJSON形式に変換し、結果を取得します。
エラーが発生した場合は、catchブロックでエラーハンドリングを行います。

POSTリクエストの送信

POSTリクエストを送信してサーバーにデータを送信する例を示します。

fetch("https://jsonplaceholder.typicode.com/posts", {
method: "POST",
headers: {
"Content-Type": "application/json",
},
body: JSON.stringify({ title: "foo", body: "bar", userId: 1 }),
})
.then((response) => {
if (!response.ok) {
throw new Error("Network response was not ok");
}
return response.json();
})
.then((data) => {
console.log(data);
})
.catch((error) => {
console.error("There was a problem with the fetch operation:", error);
});

この例では、fetch関数にオプションオブジェクトを渡すことで、
POSTリクエストを設定しています。
headersオプションでリクエストヘッダーを設定し、bodyオプションで送信するデータを指定します。

Fetch APIの利点

Fetch APIには、以下の利点があります。

シンプルなAPI

Fetch APIは、fetch関数を呼び出すだけで非同期通信が可能であり、
PromiseベースのAPIを提供するため、直感的で簡潔なコードを書けます。

ネストが少ない

Fetch APIはPromiseベースで動作するため、AJAXのようなコールバックのネストが少なく、
コードの可読性が向上します。

強力なエラーハンドリング

Fetch APIでは、ネットワークエラーやレスポンスのステータスコードに
応じたエラーハンドリングが容易です。

Fetch APIの制限

一方、Fetch APIにはいくつかの制限や欠点もあります。

ネットワークエラーのみを検出

Fetch APIは、HTTPステータスコードに関わらず、ネットワークエラーのみをcatchブロックで処理します。
レスポンスのステータスコードがエラーでも、fetchは解決されたPromiseを返します。
これは、手動でresponse.okプロパティをチェックする必要があることを意味します。

古いブラウザでのサポート

Fetch APIは、古いブラウザ(例: Internet Explorer)ではネイティブにサポートされていません。
そのため、古いブラウザでも対応させる必要がある場合は、ポリフィルを使用する必要があります。

AJAXとFetch APIの比較

AJAXとFetch APIの違いを理解するために、いくつかのポイントで比較してみましょう。

APIのシンプルさと可読性

AJAX(XMLHttpRequest)は、強力で柔軟性のあるツールですが、そのAPIは複雑で、
コードが冗長になりがちです。
一方、Fetch APIはシンプルで直感的なAPIを提供し、Promiseを使用することでネストが少なく、
可読性が高いコードを実現します。

エラーハンドリング

AJAXでは、エラーハンドリングが複雑になりがちです。XMLHttpRequestのステータスコードを手動でチェックし、エラーハンドリングを実装する必要があります。
Fetch APIでは、エラーハンドリングがより簡単ですが、ネットワークエラーのみを検出するため、
レスポンスのステータスコードに基づくエラーハンドリングが必要です。

機能の拡張性

AJAXは、フォームデータの送信やファイルのアップロードなど、
特定のタスクに対して最適化されていますが、モダンなJavaScriptの機能には対応していません。
一方、Fetch APIは、Promiseやasync/awaitと自然に統合され、
モダンなJavaScriptの機能を活用したコードを簡単に書くことができます。

ブラウザサポート

AJAXは、長年にわたり広くサポートされており、ほとんどのブラウザで使用できます。
Fetch APIは、最新のブラウザでサポートされていますが、
古いブラウザではネイティブにサポートされていないため、ポリフィルが必要になる場合があります。

非同期通信の設計パターンとベストプラクティス

非同期通信を効果的に設計するためには、いくつかのベストプラクティスを理解し、
それを適用することが重要です。

エラーハンドリングの重要性

非同期通信においては、エラーハンドリングが特に重要です。
ネットワークエラーやサーバーエラーが発生する可能性が常にあるため、
適切なエラーハンドリングを行い、ユーザーにわかりやすいエラーメッセージを表示するようにします。

例: Fetch APIでのエラーハンドリング

fetch("https://jsonplaceholder.typicode.com/posts")
.then((response) => {
if (!response.ok) {
throw new Error(`HTTP error! status: ${response.status}`);
}
return response.json();
})
.then((data) => {
console.log(data);
})
.catch((error) => {
console.error("There was a problem with the fetch operation:", error);
});

非同期処理のチェーン

非同期処理をチェーンで繋げることで、複数の非同期操作を順番に実行することができます。
これにより、コードの可読性が向上し、エラーハンドリングも一箇所でまとめて行えます。

例: Fetch APIによるチェーン処理

fetch("https://jsonplaceholder.typicode.com/posts")
.then((response) => {
if (!response.ok) {
throw new Error(`HTTP error! status: ${response.status}`);
}
return response.json();
})
.then((data) => {
console.log("First request data:", data);
return fetch("https://jsonplaceholder.typicode.com/users");
})
.then((response) => {
if (!response.ok) {
throw new Error(`HTTP error! status: ${response.status}`);
}
return response.json();
})
.then((data) => {
console.log("Second request data:", data);
})
.catch((error) => {
console.error("There was a problem with the fetch operation:", error);
});

非同期処理の並列実行

複数の非同期操作を並列に実行する場合、Promise.allを使用することで、
すべての操作が完了するまで待機し、その結果をまとめて処理することができます。

例: Promise.allによる並列処理

const fetchPosts = fetch("https://jsonplaceholder.typicode.com/posts").then((response) =>
response.json()
);
const fetchUsers = fetch("https://jsonplaceholder.typicode.com/users").then((response) =>
response.json()
);

Promise.all([fetchPosts, fetchUsers])
.then((results) => {
const [posts, users] = results;
console.log("Posts:", posts);
console.log("Users:", users);
})
.catch((error) => {
console.error("There was a problem with the fetch operation:", error);
});

まとめ

AJAXとFetch APIは、JavaScriptで非同期通信を行うための主要な手段です。
AJAXは従来の技術であり、XMLHttpRequestを使って非同期通信を実装しますが、
そのAPIはやや複雑で、コードが冗長になりがちです。
一方、Fetch APIはPromiseベースのモダンなAPIであり、簡潔で直感的なコードを提供します。

Fetch APIを使用することで、非同期通信がよりシンプルに、かつエラーハンドリングが容易になります。
しかし、古いブラウザでのサポートが必要な場合には、ポリフィルを使用する必要があります。

いずれの方法を選択するにしても、非同期通信においては適切なエラーハンドリングや非同期処理の設計パターンを理解し、これを実践することが重要です。
これにより、ユーザーにとって快適で信頼性の高いウェブアプリケーションを提供できるようになります。