XMLHttpRequest
は同期および非同期通信をサポートしています.しかし,一般にパフォーマンス上の理由により非同期リクエストが同期リクエストより選好されるべきです.
手短かにいえば,同期リクエストはプログラムの実行をブロックし.画面の「フリーズ」やユーザーインターフェイスの無反応状態を生み出します.
非同期リクエスト
機能拡張(extension)からXMLHttpRequest
を利用するのであれば,それは非同期でなければなりません.この場合,データを受信した時点でコールバックを受取ることになります.そのためリクエストが取扱われている間ブラウザは普通に動作することができます.
例: コンソールログへファイルを送信する
最も簡単な非同期XMLHttpRequest
の使用法を示します.
var xhr = new XMLHttpRequest(); xhr.open("GET", "/bar/foo.txt", true); xhr.onload = function (e) { if (xhr.readyState === 4) { if (xhr.status === 200) { console.log(xhr.responseText); } else { console.error(xhr.statusText); } } }; xhr.onerror = function (e) { console.error(xhr.statusText); }; xhr.send(null);
リクエストが非同期に取扱われるべきことを指示するため.行2で第3引数に true
を指定しています.
行3はイベントハンドラ関数オブジェクトを生成し,リクエストのonload
属性にアサインしています.このハンドラは,行4でトランザクションが完了したかどうかを知るためリクエストのreadyState
を監視し,完了済み,かつHTTPステータスが200なら受信内容をコンソール出力します.エラー発生時にはエラーメッセージが表示されます.
行15は実際にリクエストを開始します.コールバックルーチンは,リクエストの状態が変更される都度呼び出されます.
例: 外部ファイルを読込む標準的な関数を作成する
場合によっては,多くの外部ファイルを読込まねばなりません.これは読込まれたファイルの内容を指定されたリスナに切換えるためにXMLHttpRequest
オブジェクトを非同期に利用する標準的な関数です.
function xhrSuccess () { this.callback.apply(this, this.arguments); } function xhrError () { console.error(this.statusText); } function loadFile (sURL, fCallback /*, argumentToPass1, argumentToPass2, etc. */) { var oReq = new XMLHttpRequest(); oReq.callback = fCallback; oReq.arguments = Array.prototype.slice.call(arguments, 2); oReq.onload = xhrSuccess; oReq.onerror = xhrError; oReq.open("get", sURL, true); oReq.send(null); }
使用法 :
function showMessage (sMsg) { alert(sMsg + this.responseText); } loadFile("message.txt", showMessage, "New message!\n\n");
行1ではファイルが読込まれた時点で,第3引数以降の全引数をcallback関数に渡す関数を宣言しています.
行45ではイベントハンドラ関数オブジェクトを生成し,リクエストのonload
属性にアサインしています.このハンドラはトランザクションが完了したことを知るため,リクエストのreadyState
を監視します.トランザクションが完了し,かつ,HTTPステータスコードが200ならコールバック関数が呼び出されます.
エラーが発生すれば,エラーメッセージが表示されます.
行1311ではリクエストを非同期に取扱うべきことを指示すため第3引数にtrue
を指定しています.
行1412で実際にリクエストを開始しています.
例: タイムアウトの利用
読込みが行なわれるのを待つ間,プログラムが永久にハングすることを防ぐためタイムアウトを利用できます.以下のコードに示すように,XMLHttpRequest
オブジェクト上のtimeout
プロパティの値を設定することで行なわれます.
var args = arguments.slice(2); var xhr = new XMLHttpRequest(); xhr.ontimeout = function () { console.error("The request for " + url + " timed out."); }; xhr.onload = function() { if (xhr.readyState === 4) { if (xhr.status === 200) { callback.apply(xhr, args); } else { console.error(xhr.statusText); } } }; xhr.open("GET", url, true); xhr.timeout = timeout; xhr.send(null); }
ontimeout
ハンドラをセットすることで「タイムアウト」イベントを取扱うコードを追加していることに留意して下さい.
使用法:
function showMessage (sMsg) { alert(sMsg + this.responseText); } loadFile("message.txt", 2000, showMessage, "New message!\n");
ここではタイムアウトを2000ミリ秒に設定しています.
注記: timeout
のサポートは Gecko 12.0において追加されました.
同期リクエスト
稀ではありますが,同期法の利用が非同期法より望ましいことがあります.
例: HTTP 同期リクエスト
本例では簡単な同期リクエストをどのように生成するのかを示します.
var request = new XMLHttpRequest(); request.open('GET', '/bar/foo.txt', false); // `false` makes the request synchronous request.send(null); if (request.status === 200) { console.log(request.responseText); }
行3でリクエストを送信しています.null
引数はGET
リクエストにボディコンテントが必要ないことを示しています.
行5ではトランザクション完了後,ステータスコードをチェックしています.結果のコードが200(HTTPのOK)ならドキュメントのテキスト内容がコンソール出力されます.
例: Worker
からの同期HTTPリクエスト
同期リクエストが通常,コード実行をブロックしない稀な例として,Worker
内でのXMLHttpRequest
の利用があります.
example.html
(主ページ):
<!doctype html> <html> <head> <meta http-equiv="Content-Type" content="text/html; charset=UTF-8" /> <title>MDN Example</title> <script type="text/javascript"> var worker = new Worker("myTask.js"); worker.onmessage = function(event) { alert("Worker said: " + event.data); }; worker.postMessage("Hello"); </script> </head> <body></body> </html>
myFile.txt
(XMLHttpRequest
同期呼出しのターゲット):
Hello World!!
myTask.js
( Worker
です):
self.onmessage = function (event) { if (event.data === "Hello") { var xhr = new XMLHttpRequest(); xhr.open("GET", "myFile.txt", false); // 同期リクエスト xhr.send(null); self.postMessage(xhr.responseText); } };
Worker
を使っているため効果は非同期になります.それはサーバーとバックグラウンドでやり取りしたり,コンテントを予めロードしたりするので有用でしょう.例と詳細については Using web workers を参照下さい.
同期リクエストでなければならない場合
XMLHttpRequestを同期的に使用するしかない場合があります.例えば,これはwindow.onunload
とwindow.onbeforeunload
イベントの間に起こります.(下記も参照).
ページがアンロードされた時点で通常のXMLHttpRequestを送信することは,サーバーからの非同期レスポンスに付随する問題を招きます:レスポンスが返って来るまでに,ページはアンロード済みで,コールバック関数はもはや存在しません.これはJavaScriptの “function is not defined”エラーを生み出します.
A possible solution is to make sure that any AJAX requests that you make on unload are make synchronously instead of asynchronously. This will ensure that the page doesn’t finish unloading before the server response comes back.
例#1:exit前の自動ログアウト
window.onbeforeunload = function () { var xhr = new XMLHttpRequest(); xhr.open("GET", "logout.php?nick=" + escape(myName), false); // 同期リクエスト xhr.send(null); if (xhr.responseText.trim() !== "logout done"); { // "logout done" は期待されるレスポンステキストです return "Logout has failed. Do you want to complete it manually?"; } };
例 #2: ユーザーがリンクをクリックすることも退屈なベージを登録することもなくサイトを去る場合,注意すること
<!doctype html> <html> <head> <meta http-equiv="Content-Type" content="text/html; charset=UTF-8" /> <title>MDN Example</title> <script type="text/javascript"> (function () { function dontPanic () { bForsake = false; return true; } onload = function () { for ( var aLinks = document.links, nLen = aLinks.length, nIdx = 0; nIdx < nLen; aLinks[nIdx++].onclick = dontPanic ); }; onbeforeunload = function () { if (bForsake) { /* silent synchronous request */ var oReq = new XMLHttpRequest(); oReq.open("GET", "exit.php?leave=" + escape(location.href), false); oReq.send(null); } }; var bForsake = true; })(); </script> </head> <body> <p><a href="https://developer.mozilla.org/">Example link</a></p> </body> </html>