JavaScriptによる非同期通信の仕組み

 ここまでは,Ajaxを支える技術としてJavaScriptでユーザー・インタフェースを実装する仕組みを紹介した。続いて,JavaScriptによる通信の実装について見ていこう。よく見かけるAjaxの紹介記事は,この通信の実装方法に重きを置いていることが多い。なぜなら,Ajaxという方法論が普及する前は,JavaScriptのコードから通信をしようなどとと考える人はおそらく少数派だったからであろう。

 JavaScriptでHTTP通信を行うには,一般にXMLHTTP,またはXMLHttpRequestと呼ばれるオブジェクトを利用する。ただし,注意しておくことが二つある。一つは,XMLHttpRequestの実装方法はブラウザの種類やバージョンによって異なるという点,もう一つは,「クロスドメインの制限」と言って,現在表示しているHTMLを取得したサイトと異なるドメインのサーバーとは通信できないという制限である。

 もう少し詳しく説明しよう。まず,XMLHttpRequestのブラウザ依存性についてである。図6をご覧いただきたい。Internet Explorer(IE)ならActiveXオブジェクトを生成し,それ以外はJavaScriptにビルトインされているXMLHttpRequestオブジェクトを生成する,というのが基本的な方法論である*4。ただし,図6の方法で生成したXMLHttpRequestオブジェクト自体の仕様(API)は,ベンダーごとの拡張機能があるものの,ブラウザの種類にほぼ依存しないと考えてよい。

図6●JavaScriptでHTTP通信を行うために生成するオブジェクト。Microsoft.XMLHTTPとMsxml2.XMLHTTPの両者が利用できる環境においては,前者のほうが高い性能になる可能性があると言われている
図6●JavaScriptでHTTP通信を行うために生成するオブジェクト。Microsoft.XMLHTTPとMsxml2.XMLHTTPの両者が利用できる環境においては,前者のほうが高い性能になる可能性があると言われている

 というのも,本来このインタフェースを最初に実装したのはマイクロソフトであり,MozillaやOperaはその仕様に基づいて実装した,という経緯があるからだ。細かいことはさておき,オブジェクトを生成する部分だけをブラウザによって異なるようにしてやれば,大部分のブラウザで動作するというわけだ。

 次はもう一つの問題,クロスドメインの制限について考察しよう。この制限事項については,よく「セキュリティのため」という一言で済まされていることが多いが,要するに,悪意のあるJavaScriptコードの蔓延を防ぐための配慮である*5。この制限がどれだけ深刻であるかは,どのようなシステムを構築するかにかかっている。例えば,あちこちのサイトと通信を行って,その結果を一つのHTMLに表示するようなシステムを構築しようとしたらクロスドメインの制限に行き当たるだろう。

 クロスドメインの制限について,誰もが満足するような回避方法は今のところない*6。ただし,クロスドメインの必要がなければ,つまり,表示しているHTMLを取得したサイトと通信するだけなら全く問題にはならない。そういうわけで,この記事ではクロスドメインの制限を避けて通る道を選んでいくことにする。

ブラウザの表示をサーバーから取得した文字列で書き換える

 前置きがかなり長いので,そろそろコードが読みたくなってきただろうか。それではサンプル・プログラムをご覧いただこう。

 図3のサンプルを,サーバー通信を行うように変更したサンプルである。先ほどは,ボタンを押すとテキストボックスにタイプした文字列でブラウザの一部を書き換えたが,今度は通信を使ってサーバー上のテキスト・ファイルから取得した文字列で書き換えている(図7)。コードはリスト3リスト4リスト5リスト6の通りである。

図7●ボタンを押すとサーバーから取得した文字列で画面を書き換えるサンプル
図7●ボタンを押すとサーバーから取得した文字列で画面を書き換えるサンプル。こちらからソースをダウンロードできます
[画像のクリックで拡大表示]

リスト3●図7のHTMLドキュメント(page1.htm)。文字コードはUTF-8
リスト3●図7のHTMLドキュメント(page1.htm)。
文字コードはUTF-8
 [画像のクリックで拡大表示]
リスト4●サーバー上に配置するテキスト・ファイル(mydata.txt)の内容。文字コードはUTF-8
リスト4●サーバー上に配置するテキスト・ファイル(mydata.txt)の内容。文字コードはUTF-8

リスト5●XMLHttpRequestのブラウザ依存を解決するためのコード(xmlhttprequest.js)
リスト5●XMLHttpRequestのブラウザ依存を解決するためのコード(xmlhttprequest.js)

リスト6●ボタンを押したときに動作するコード(myname.js)
リスト6●ボタンを押したときに動作するコード(myname.js)

 これらのファイルは,すべてUTF-8の文字コードで作成している。シフトJISや日本語EUCではうまく動作しない可能性がある。詳細な説明は省略するが,「一般的にXMLHttpRequestを使う場合はUTF-8にしておくのが無難」ということを覚えておくとよい。なお,HTMLファイル(page1.htm)とテキスト・ファイル(mydata.txt)は同じディレクトリに置き,JavaScript(xmlhttprequest.js,myname.js)はその下にサブ・ディレクトリjsを作成し,そこに配置しておく。

 リスト3(page1.htm)の(1)では,このHTMLがUTF-8でエンコーディングしてあることを宣言している。(2)では,二つのJavaScriptコードを読み込んでいる。XMLHttpRequestのブラウザ依存を解決するためのコードであるxmlhttprequest.js(リスト5)と,ボタンを押したときに動作するコードであるmyname.js(リスト6)である。

 xmlhttprequest.jsについて説明しよう。ブラウザ依存に対応するコードのスタイルはいくつかあるが,ここではIE(6まで)の場合にも図6の(3)のような呼び出しができるように,JavaScriptで細工をしている。具体的には,JavaScriptがネイティブのXMLHttpRequestをサポートしておらず,かつActiveXObjectメソッドをサポートしていたら(1),JavaScriptでXMLHttpRequestという名前の関数を実装するようにしている(2)。関数の中身は,図6(2)の方法でオブジェクトの生成を試みた後(3),図6(1)の方法でオブジェクトの生成を試みる(4)。そして,上記の両方とも失敗したらnullを返す,といった具合である。

 次にmyname.jsである。関数change_mynameは図3のサンプルと全く同じなのでここでは説明しない。ボタンを押したときに呼び出される関数は,getdata_mynameである(リスト3の(3))。この関数では,XMLHttpRequestオブジェクトを生成し(1),続いてオブジェクトの初期化をしてから(2)~(3),実際の通信要求を行っている(4)。

 (4)のxmlhttp.send(null)のメソッド呼び出しは,すぐに呼び出し元に戻ってくる。実際の通信処理はバックグラウンドで行われるのである。だから(4)は「通信を行う」ではなく「通信要求を行う」なのである。この,バックグラウンドで通信を行うという機能が,まさしくAjaxの「A」=「Asynchronous(非同期)」の部分である。

 リスト6の(2)は,mydata.txtというURLに対してHTTP GET要求を出してくれ,という意味である*7。(3)では,通信の状態(「通信中」とか「通信完了」など)が変化したときにオブジェクトから呼び出してもらう関数*8を渡している。処理の内容は,通信の状態(xmlhttp.readyState)が「完了(値が4)」で,かつHTTPの応答コード(xmlhttp.status)が正常終了(値が200)なら,通信で取り込んだデータ全体(xmlhttp.responseText)を引数にしてchange_mynameを呼び出す,というものである。

 この例では,通信先のURLとしてmydata.txtというテキスト・ファイルを指定している。これがCGIやPHPスクリプトのURLであれば,サーバーと連携して動的にデータを持ってくることができる,というわけである。

ブラウザ依存性をさらに回避するために

 世間のブラウザのシェアから見れば,リスト5に示したコードは8割がた,いや,ひょっとすると9割以上のユーザーに対してうまく動作するはずである。しかし,もっと徹底したい,最大限の利用者のために最良の解を提供したい,というよくばりな開発者もいるに違いない。そういう人におすすめしたいのがAndrew Gregory氏が開発したxmlhttprequest.jsである*9

 実は,リスト5のコードで使っている「IE環境下でnew XMLHttpRequestをJavaScriptで実装するトリック」は,このコードからいただいたアイデアである。JavaScriptのファイル名を同じにしてあるのも,同じ理由による。つまり,リスト5のファイルを,Andrew Gregory氏のサイトからダウンロードしたxmlhttprequest.jsに差し替えても,そのまま動く。

 xmlhttprequest.jsプログラムの最新版は2005年9月28日であり,こんなに古くて大丈夫なのかとか,作者の情熱が冷めてしまって更新が止まっているのではないか,などと不安になるかもしれないが,心配はいらない。ブラウザを供給しているベンダーの対応により,コードを書き直す必要がなくなっているだけである。

 xmlhttprequest.jsは図6の(3)の形式「new XMLHttpRequest」をJavaScriptで実装している。メジャーなJavaScriptライブラリは,大抵の場合まずXMLHttpRequestが利用可能かどうかを調べてからブラウザ依存に対応するようになっている。したがって,ブラウザ依存性を回避したいと思ったら,まずAndrew Gregory氏のxmlhttprequest.jsを読み込めばよいというわけだ。

 もう少し具体的に説明しよう。例えばJavaScriptのフレームワークとして広く使われているprototype.js(http://www.prototypejs.org/)の中にも,XMLHttpRequestのブラウザ依存を回避するコードがある。ただし,ブラウザ依存の判断方法として,最初に「XMLHttpRequestが存在するかどうか」をチェックしている。そのため,prototype.jsの前にxmlhttprequest.jsを読み込んでおけば,prototype.js側では「new XMLHttpRequest」が使われるようになり,これによってxmlhttprequest.jsのコードが呼び出される,というわけだ。

最後に

 Ajaxを支える技術について見てきたが,どうだろうか。進歩的な読者から見れば,まだそんなことをやっているのか,と思うかもしれない。いまやAjaxのフレームワークは,星の数と言っていいほどあり,中にはDOMのリファレンスを片手にコードを書いたり,ブラウザ依存性を気にしたりしなくても済むような秀逸なフレームワークも存在する。

 しかし,Ajaxの普及はまだ始まったばかりである。フレームワークは山ほどある。あるフレームワークが自分の要件あるいは目的に合うかどうかを評価したり,想定外の問題に行きあたってトラブル・シューティングが必要になったり,あるいは既存のフレームワークに自分のアイデアをプラグインしたいと思ったとき,ここで紹介した知識はけっして無駄にならないと思う。

 なお,Ajaxの短期集中連載は,今回をもっていったん終了する。Ajaxの本当に基礎的な部分はご理解いただけたのではないかと思う。しばらく間をあけて今度は,もう少し高度な技術について再び集中連載する予定である。