XMLHttpRequest 同期 / 非同期について
前回までの投稿で、XMLHttpRequestを使ったAJAX通信の動きを試してみました。
通常、通信の処理には、同期と非同期という考え方があります。
同期はクライアント側からサーバに対して通信をしている間、リクエストに対してのレスポンスを待ち、
レスポンスが返ってきてからクライアント側の処理を続行します。
また、非同期はクライアント側からサーバに対して通信を行うと、
その結果(レスポンス)を待たず、クライアント側の処理を続行します。(レスポンスはサーバ側の処理が終わり次第に返ってくるイメージです)
では、前回に書いたサンプルソースは同期か非同期のどちらになっているかを確かめます。
クライアント側のjavascriptは、前回に投稿したサンプルと概ね同じですが、
req.send()メソッドの前に、非同期中の動きを確認する文字列を出力するように変更しています。
result.innerHTML += '同期 / 非同期の確認' + '<br />'; console.log('同期 / 非同期の確認'); req.send();
また、サーバ側のphpは、時間のかかる処理を行うものとし、「sleep(2);」という2秒のスリープ時間を入れました。
サンプルの全体は以下のように書いています。
<!DOCTYPE html> <html lang="ja"> <head> <meta charset="UTF-8"> <title>テストHTML</title> </head> <body> <input type="button" id="sampleXMLHttpRequest" value="レスポンスのテスト"> <div>通信時の処理内容</div> <div id="ajax_result"></div> <script type="text/javascript"> // ボタン要素のDOMを取得 let element_sampleXMLHttpRequest = document.getElementById('sampleXMLHttpRequest'); // イベントを付与 element_sampleXMLHttpRequest.addEventListener('click', SendXMLHttpRequest, false); // ボタン押下時の処理 function SendXMLHttpRequest() { // 通信処理を画面に出力する為、DOM操作 let result = document.getElementById("ajax_result"); // ajax通信処理 let req = new XMLHttpRequest(); req.open("GET", "./response.php"); req.onreadystatechange = function() { // 検証の為、画面出力とログ出力をする result.innerHTML += 'onreadystatechange start' + '<br />'; result.innerHTML += 'req.readyState -> ' + req.readyState + '<br />'; result.innerHTML += 'req.status -> ' + req.status + '<br />'; console.log('req.readyState -> ' + req.readyState); console.log('req.status -> ' + req.status); // レスポンスの状態 (req.readyState) を判定する if (req.readyState == 4) { result.innerHTML += 'レスポンスは正常 (readyState)' + '<br />'; console.log('レスポンスは正常 (readyState)'); } else { result.innerHTML += 'レスポンスは異常の為、処理終了(readyState) ' + '<br />'; console.log('レスポンスは異常の為、処理終了(readyState) '); return false; } // レスポンスの状態 (req.status) を判定する if (req.status == 200) { result.innerHTML += 'レスポンスは正常 (status)' + '<br />'; console.log('レスポンスは正常 (status)'); } else { result.innerHTML += 'レスポンスは異常の為、処理終了(status) ' + '<br />'; console.log('レスポンスは異常の為、処理終了(status) '); return false; } // getResponseHeaderを取得する let content_type = req.getResponseHeader("Content-Type"); result.innerHTML += 'content_type -> ' + content_type + '<br />'; console.log('content_type -> ' + content_type); // レスポンスの状態 (content_type) を判定する // ここでは検証の為、強制的に「text/html; charset=UTF-8」の文字列で判定する if (content_type == "text/html; charset=UTF-8") { result.innerHTML += 'レスポンスは正常 (content_type)' + '<br />'; console.log('レスポンスは正常 (content_type)'); } else { result.innerHTML += 'レスポンスは異常の為、処理終了(content_type) ' + '<br />'; console.log('レスポンスは異常の為、処理終了(content_type) '); return false; } // responseレスポンス内容を出力 result.innerHTML += 'レスポンス内容の表示 -> ' + req.response + '<br />'; }; result.innerHTML += '同期 / 非同期の確認' + '<br />'; console.log('同期 / 非同期の確認'); req.send(); } </script> </body> </html>
サーバ側の response.php では、2秒のスリープを追記。
<?php sleep(2); echo "response_test_123"; ?>
実際にアクセスして、画面上の「レスポンスのテスト」ボタンを押下すると、
まず、画面上に「同期 / 非同期の確認」が表示されます。
その後に、サーバ側の処理が約2秒後に終わり、レスポンスが返りクライアント側に結果が渡され、
画面上にレスポンス結果の文字列が表示されます。
このことから、前回のサンプルプログラムは、サーバ側の処理を待たずに通信後に
javascriptの処理を続行する「非同期」で処理されていることがわかります。
XMLHttpRequest 同期について
では次に、上記のサンプルのソースを調整して、同期通信を試してみます。
同期通信にする為には、openメソッドの第三引数にfalseを設定します。
// 第三引数にfalseを指定することで、同期で実行する req.open("GET", "./response.php", false);
上記のように書くことで、クライアント側からサーバ側へ通信し、
サーバ側での処理結果(レスポンス)を待ってから、クライアント側の処理が続行されます。
サンプル全体は以下のように書きました。
<!DOCTYPE html> <html lang="ja"> <head> <meta charset="UTF-8"> <title>テストHTML</title> </head> <body> <input type="button" id="sampleXMLHttpRequest" value="レスポンスのテスト"> <div>通信時の処理内容</div> <div id="ajax_result"></div> <script type="text/javascript"> // ボタン要素のDOMを取得 let element_sampleXMLHttpRequest = document.getElementById('sampleXMLHttpRequest'); // イベントを付与 element_sampleXMLHttpRequest.addEventListener('click', SendXMLHttpRequest, true); // ボタン押下時の処理 function SendXMLHttpRequest() { // 通信処理を画面に出力する為、DOM操作 let result = document.getElementById("ajax_result"); // ajax通信処理 let req = new XMLHttpRequest(); // 第三引数にfalseを指定することで、同期で実行する req.open("GET", "./response.php", false); req.onreadystatechange = function() { // 検証の為、画面出力とログ出力をする result.innerHTML += 'onreadystatechange start' + '<br />'; result.innerHTML += 'req.readyState -> ' + req.readyState + '<br />'; result.innerHTML += 'req.status -> ' + req.status + '<br />'; console.log('req.readyState -> ' + req.readyState); console.log('req.status -> ' + req.status); // レスポンスの状態 (req.readyState) を判定する if (req.readyState == 4) { result.innerHTML += 'レスポンスは正常 (readyState)' + '<br />'; console.log('レスポンスは正常 (readyState)'); } else { result.innerHTML += 'レスポンスは異常の為、処理終了(readyState) ' + '<br />'; console.log('レスポンスは異常の為、処理終了(readyState) '); return false; } // レスポンスの状態 (req.status) を判定する if (req.status == 200) { result.innerHTML += 'レスポンスは正常 (status)' + '<br />'; console.log('レスポンスは正常 (status)'); } else { result.innerHTML += 'レスポンスは異常の為、処理終了(status) ' + '<br />'; console.log('レスポンスは異常の為、処理終了(status) '); return false; } // getResponseHeaderを取得する let content_type = req.getResponseHeader("Content-Type"); result.innerHTML += 'content_type -> ' + content_type + '<br />'; console.log('content_type -> ' + content_type); // レスポンスの状態 (content_type) を判定する // ここでは検証の為、強制的に「text/html; charset=UTF-8」の文字列で判定する if (content_type == "text/html; charset=UTF-8") { result.innerHTML += 'レスポンスは正常 (content_type)' + '<br />'; console.log('レスポンスは正常 (content_type)'); } else { result.innerHTML += 'レスポンスは異常の為、処理終了(content_type) ' + '<br />'; console.log('レスポンスは異常の為、処理終了(content_type) '); return false; } // responseレスポンス内容を出力 result.innerHTML += 'レスポンス内容の表示 -> ' + req.response + '<br />'; }; result.innerHTML += '同期 / 非同期の確認' + '<br />'; console.log('同期 / 非同期の確認'); req.send(); } </script> </body> </html>
画面にアクセスして「レスポンスのテスト」ボタンを押すと、先程のサンプルとは違い
「同期 / 非同期の確認」という文字列は、ボタン押下と同時には画面に出てきません。
サーバ側の処理が終わり、約2秒経過した後に「同期 / 非同期の確認」の文字列と同時に
レスポンス結果の文字列が画面に出力されます。
このようにopenメソッドの第三引数により、同期か非同期かを決定することができます。
また、第三引数を省略すると「非同期」として処理されます。
このことから同期処理を行うと、サーバ側の処理結果に合わせたタイミングで
クライアント側の動作をコントロールすることができます。
実際の開発では、同期/非同期のどちらを使うかはケースによって決めます。
XMLHttpRequest 同期 / 非同期の注意点
ここまででXMLHttpRequestの同期/非同期を試してみましたが、
現在の仕様では同期は廃止(非推奨)となっています。
注: Gecko 30.0 (Firefox 30.0 / Thunderbird 30.0 / SeaMonkey 2.27), Blink 39.0, Edge 13 以降では、メインスレッド上での同期リクエストは ユーザーの使い勝手に悪影響を与えるため、非推奨になっています。
(mdn web dosc [同期と非同期のリクエスト]から引用)
XMLHttpRequest自体、現在の開発手法では使うケースが極端に少なく、
当ブログではXMLHttpRequestの挙動を試す意味で取り上げています。
実際の開発シーンでは、フレームワークやライブラリ側が提供している通信処理の書き方をするケースが多いです。