JavaScript中級者への道!Ajaxを使いこなすの巻

JavaScript
JavaScript

JavaScript強化期間もいよいよ終盤戦です。
今回は、Ajaxについて学んでいきます。

こちらのJavaScriptロードマップも併せてご覧ください。

目指せ中級!JavaScript学習ロードマップ(非webエンジニア向け)
当ブログの筆者のinashunです。 私はJavaをメインの技術としてお仕事をしているのですが、最近お仕事でJavaScriptを扱う機会が増えてきました。 これまでも扱う機会は多々あったのですが、すべて「既存ソースの改修」か「実装...

Ajaxとは

AjaxはAsynchronous JavaScript And XMLの略です。ざっくり説明すると「画面遷移しなくてもJavaScriptが頑張ってブラウザからサーバー処理を呼び出して結果を受け取れるよ」という技術です。

画面遷移が不要というところから、【Asynchronous】日本語に略すと非同期通信という扱いになります。
この非同期って言葉、システム開発のいろんな場面でいろんな使われ方をする言葉なので、パッと聞いてもイメージがつかみづらいですよね。マルチスレッドだとか逐次処理だとか、JavaScriptでも非同期処理という言葉はよく耳にします。もう混乱しちゃうわ。
言葉の意味を追うと果てしない戦いになるので、ここでは単純に「画面遷移を伴う通信」を「同期通信」、「画面遷移を伴わない通信」を「非同期通信」として扱うことにします。何卒。

Ajaxとは、同期通信でしかサーバと通信できなかったものを、非同期通信、つまり画面遷移なしで通信できるようにした革命的存在なのです!(それが出た当時は、の話ですが。。。)
この技術はその後のwebアプリケーションの新たな潮流を作り、HTMLやJavaScriptの大きな発展のきっかけにもなりました。というか、これが出てくるまではJavaScriptなんてブラウザにアニメーションをつけて遊ぶだけのおもちゃのような扱いだったそうです。今では考えられませんね。

こうやって聞くと、Ajaxってすごい革命的な技術なのかな?と思われるかもしれませんが、そうではありませんHTTPという通信規格とJavaScriptという言語の出来ることをうまくつなぎ合わせただけの、ただの開発技法のひとつでしかありません。仕組みを理解してしまえば、簡単に扱えるようになります。

Ajaxを支える技術

Ajaxを実現する技術要素として、「JavaScript」「HTML」「DOM」「XML」「JSON」「サーバサイドの処理」「HTTP」等があります。

JavaScriptはAjaxの処理を実現する言語です。といっても、今ではAjaxを実行するためのAPIが複数用意されているので、それを使いこなすだけです。使い方は下記にて解説しているので、それを試してみてください。

HTMLDOMは、ブラウザに表示される画面の要素を司る技術です。HTMLは画面の要素を定義するマークアップ言語、DOMは実際に表示されている要素をプログラムから扱えるようにしたオブジェクトです。このDOMを操作することで、動的な画面を実現しています。
DOMの詳細については、こちらの記事を参照ください。

JavaScript中級者への道!DOMを操作するの巻
3月は個人的にJavaScript集中強化月間としてお勉強していきます。 今回はJavaScriptでHTMLのDOM操作をする方法をまとめていきます。 こちらのJavaScriptロードマップも併せてご覧ください。 DO...

XMLJSONは、どちらも構造的なデータを表現するための記法です。Ajaxでの通信時のデータの受け渡しに用いられます。
特にXMLは複雑に見えて敬遠されがちなところがあると思いますが、結局はただの名前の付いたテキストデータでしかありません。JSONはそのことがより強調されている感じで、あまりとっつきづらさはないと思います。その結果か、現在はJSONを使ったデータの受け渡しが主流となっています。
元々XMLばかりだったためか、Ajaxという名前の語源の一部にもなっているおかげで、「XML必須なの?」という疑問がありがちかなと思いますが、そんなことはありません!XMLでもJSONでもどちらでもいいです。

サーバサイドの処理については、当記事では深くは触れません。JavaでもPythonでもRubyでもNode.jsでも、サーバサイドがどんな言語であるかは特に意識する必要はないからです。また、現在主流のwebフレームワークには漏れなくAjax通信に応えるための仕組みが用意されています。ドキュメントも豊富ですので、それらを参照してみてください。

HTTPは、webブラウザwebサーバ間の通信のルールを定義するプロトコルです。URLの先頭についている「http」や「https」といった文字列は、まさにHTTPで通信をすることを宣言するためのものなのです。
HTTPの最も基本的な役割は「ブラウザサーバにデータを送信する」⇒リクエストの送信と、「サーバブラウザに画面に表示するためのHTMLを送信する」⇒レスポンスの受信、の2つです。このリクエストとレスポンスの関係により、画面遷移を伴う同期処理が実現されます。
Ajax処理の場合は、リクエストの送信はすることは同じですが、レスポンスの受信は「サーバは画面を表すHTMLじゃなくてデータそのものを表すXML(またはJSON)を送信する。画面へはデータを受け取ったJavaScriptがDOM操作して反映する」となります。

同期通信と非同期通信の違い

同期通信と非同期通信の処理の流れの違いを図解しました。

同期通信の場合は、ブラウザが直接サーバに対してHTTPリクエストを送信します。それに対して返却されるのは新しい画面のHTMLです。ブラウザは受け取った新しいHTMLを画面に描画、つまり画面遷移を行うことでこれを表示します。

Ajaxによる非同期通信の場合、HTTPリクエストを送るのはブラウザではなくJavaScriptとなります。返却されるHTTPレスポンスは処理結果のデータを格納したJSONまたはXML形式のデータとなります。これを受け取るのもJavaScriptであり、受け取った値をDOM操作プログラムによって既に表示されているブラウザの画面に当てはめることによって表示されます。

JavaScriptでAjax通信の処理をプログラミングするところが同期通信と比較した非同期通信の大きな特徴です。このプログラミングの方法を知ることができれば、容易にAjax処理を実装することができます。
ではここからは、そのプログラミングの方法をいくつか紹介します。

XMLHttpRequest

プレーンのJavaScriptでAjax処理を行うことができる、最も基礎的な方法です。

今回は、ブラウザ上に表示されているテキストを、サーバから取得した値で画面遷移なしで置き換えるという処理を試してみます。
サーバ側の処理にはJavaのSpringを使っています。飛んでくるデータはJSON形式となります。

@GetMapping("send/path/url")
@ResponseBody
public Map<String, Object> ajax(Model model, @RequestParam(name = "data") String data) {
    System.out.println("Ajax Request data : " + data);
    Map<String, Object> paramMap = new HashMap<String, Object>();
    paramMap.put("value", "Hello Ajax World");
    return paramMap;
}
<p id="text">Hello World!</p>
<button id="ajax" type="button">Ajax send!</button>
  • これより先のjQueryとFetch APIの場合も、サーバ側のソースとHTMLは同じものを使用します。

XMLHttpRequestを用いたAjax処理の実装方法です。

document.querySelector("#ajax").addEventListener("click", () => {

    const xhr = new XMLHttpRequest();
    xhr.open("GET", "send/path/url?data=XHR", true);

    xhr.addEventListener("readystatechange", () => {
        if (xhr.readyState == 4 && xhr.status == 200) {
            const data = JSON.parse(xhr.response);
            document.querySelector("#text").innerText = data.value;
        }
    });
    xhr.send();
});

new XMLHttpRequest()でオブジェクトを生成し、そのオブジェクトのメソッドを適宜呼び出すことによってAjax処理を記述していきます。変数名のxhrXMLHttpRequestのイニシャルの略で、慣例的にこのオブジェクトを表す変数名として使用されます。

xhr.open()メソッドでリクエスト情報を設定します。
第一引数にはHTTPメソッド(GET・POSTなど)を指定します。
第二引数にはURLを指定します。
第三引数は非同期モードで通信するかどうかをbooleanで指定します。trueにすると非同期リクエストとなり、通信中でも画面操作が可能になります。Ajaxの挙動としてあるべき姿はこちらなので、とくに理由がなければtrueにしましょう。falseにすると、同期リクエストとなります。この場合は通信を行っている間は画面を操作することができません。

xhr.addEventListener()メソッドで通信が完了した後の処理をコールバック関数として定義します。イベント名はreadystatechangeを指定します。
サーバから受け取ったレスポンスボディは、xhr.responseプロパティに格納されます。今回はJSONで受け取っているので、プロパティに格納されているJSON文字列をパースしてデータオブジェクトに変換してから画面項目に当てはめています。

イベントリスナーの関数内のif (xhr.readyState == 4 && xhr.status == 200)重要です。readystatechangeイベントは処理の完了時のみ発火するわけではありません。ですので、この処理を入れないと意図しないタイミングで処理が走ってしまいます。
readyState4はxhrの通信が完了したことを表すステータスです。statusにはHTTP通信自体のステータスが入ります。
XMLHttpRequest.readyState – Web API | MDN
HTTP レスポンスステータスコード – HTTP | MDN

xhr.send()メソッドを呼び出すことで通信を実行します。
サーバにデータを送信する場合、今回はGET通信を使用しているのでURLにリクエストパラメータとしてデータを設定しています。(?data=XHRの部分)
POSTなどの通信方法を使用する場合は、このメソッドの引数にデータを定義した文字列やFormDataオブジェクトなどのデータオブジェクトを設定することで送信ができます。

基本的な流れはこのようになっています。メソッドやURLを設定して、通信終了後のコールバック関数を定義して、通信を実行する、という流れのイメージは他の方法でも基本的に共通です。

非同期処理について

通信終了後の処理をコールバック関数として定義するのは、実際に処理が実行される順番がスースコードに書いてある順番通りにならないためです。そういった処理のことを非同期処理と呼びます。イベントハンドラなど様々なところでも使われている処理方法です。非同期処理を実現する方法はコールバック関数だけではありません。その詳細については別途まとめることにします。

jQuery

みんな大好き便利なライブラリjQueryを用いたAjax通信の方法です。

JavaScript中級者への道!jQueryをそこはかとなく知るの巻
この3月をJavaScript強化月間としていろいろ学習しています。 今回はまだまだ現役なjQueryを学んでいきます。 こちらのJavaScriptロードマップも併せてご覧ください。 jQueryとは jQueryは、...
$("#ajax").on("click", () => {
    $.ajax({
        url: "send/path/url",
        type: "GET",
        data: { data: "jquery" },
        dataType: "JSON"
    }).done((data, textStatus, jqXHR) => {
        $("#text").text(data.value);
    });
});

記述方法がとても簡潔になりました。さすがですね。

Ajax通信には$.ajax()関数を使用します。
引数にはAjax通信の設定を記載したオブジェクトを渡します。例に記載の項目のほかに、dataプロパティにオブジェクトを設定することでデータを送信することができます。これはGETでもPOSTリクエストでも有効です。

通信成功時の後処理はdone()メソッドをチェーンして定義します。これは、ajax()メソッドが返却するjqXHRオブジェクトというXMLHttpRequestオブジェクトをjQueryが拡張したオブジェクトのメソッドです。処理の失敗時はfail()メソッドを使用して後処理を記述します。

  • メソッドチェーンで非同期処理を定義するための技術としてPromisedeferredといったものがあります。上記の例の方法も例にもれずこの技術の流れに沿ったものとなります。これらはJavaScriptの非同期処理を理解するうえでとても重要な要素です。

Fetch API

最後に紹介するのはFetch APIです。これはライブラリ等を使用しないバニラのJavaScriptで、XMLHttpRequestよりも簡潔にAjax通信を実装できるモダンなAPIです。IEでは動作しないのでそこをターゲットブラウザにしている場合は残念ながら、、、なのですが、それ以外のブラウザでは動作するので積極的に使っていきたいところです。

document.querySelector("#ajax").addEventListener("click", () => {

    fetch("send/path/url?" + new URLSearchParams({data: "Fetch API"}), {
        method: "GET"
    }).then((response) => {
        if(response.ok) {
            return response.json();
        } else {
            throw new Error();
        }
    }).then((json) => {
        document.querySelector("#text").innerText = json.value;
    }).catch(error => alert(error));
});

fetch()メソッドにリクエストを設定することで使用します。第一引数にはURLを、第二引数にはリクエストの設定を行うオブジェクトを設定します。GET送信の場合は送信データはURLに直接付与します。今回はURLSearchParamsオブジェクトを利用してオブジェクト形式のデータをそのまま送信できるようにしています。POST送信などの場合は第二引数のオブジェクト内でdataパラメータに設定します。

このメソッドはPromiseオブジェクトを返却します。そのオブジェクトのthen()メソッドにコールバック関数を記述することで、Ajax通信後の後続処理を記述します。

上記の例はよく見るとthen()メソッドが数珠つなぎになっています。さらにはcatch()メソッドというものまで。。。この例だと、最初のthen()response.oktrueになった場合、response.jsonがリターンされますが、そのJSONが次のthen()の引数のjsonに入ります。一方、response.okfalseと評価された場合、エラーがスローされて、catch()メソッドが呼び出されます。引数のerrorにはスローされたエラーがそのまま入ります。
この仕組みを実現しているのがPromiseという技術になります。

Promiseはもっと奥が深いのですが、その詳細な説明はまたの機会に、、、ということにさせてください。簡単にだけ説明すると、PromiseはJavaScriptの非同期処理において、複数のコールバック関数を連続して実行するような処理を直感的に実装できる仕組みです。現在のJavaScriptで非同期処理を最もきれいに表現することができる方法だと思っていてください。

このFetch APIとPromiseはES6と呼ばれるJavaScriptの比較的新しいバージョンから登場したものになります。このバージョンでは他にも便利な文法が多数追加されました。(本ブログの中でもすでにその文法が至るところで使用されています)
その内容については別の記事でまとめる予定です。

まとめ

  • Ajaxはブラウザが画面遷移なしでサーバと通信するための技術、今やシステム開発には必須の存在
  • Ajaxの仕組みはちゃんと噛み砕いて理解すれば難しいことはない
  • Ajaxを実装する方法として「XMLHttpRequest」「jQuery」「Fetch API」などがある
  • プレーンのJavaScriptでモダンな実装をしたいならFetch APIを採用しよう。元々jQueryを使っていて楽に実装したいのであればjQueryのajaxメソッドを採用しよう

参考

Ajax – Wikipedia
Ajax – 開発者ガイド|MDN
XMLHttpRequestの使用 – Web API|MDN
はじめてのAjax(jQuery) 2018年版 – Qiita
Fetchの使用 – Web API|MDN

 

コメント

タイトルとURLをコピーしました