JavaScriptはシングルスレッドの言語です。よって処理は上から順々に行われていき、並列処理はできません。(※workerを除く)
順々に行われていく処理を同期処理と呼びます。
ここで、ブラウザから外部に通信を行う処理を考えてみましょう。郵便番号による住所検索を例に挙げてみます。
よくあるUIですが、郵便番号検索ボタンを押し、外部サーバーにある郵便番号検索APIを叩いて住所を取得し、都道府県・市町村・番地を埋めるものです。
同期的に通信を行ってしまうと、郵便番号検索ボタンを押した瞬間、ブラウザが固まります。なぜなら通信という処理がまだ完了していないため、ブラウザはその処理に掛かりっきりになってしまっているからです。
それに対して非同期通信はブラウザの処理をブロックしません。処理をブラウザ外に委譲することで、自身の処理を続行できます。
非同期通信の種類
現状、ブラウザから非同期通信を行うためには以下の2つの方法があります。
- XMLHttpRequest
- Fetch API
順に説明をしていきましょう。
XMLHttpRequest(XHR)
これはJavaScriptに元々備わっているHTTP RequestのためのAPIです。
AJAXという技術の根幹となるものです。
実際にどのようにリクエストを送るのか見てみましょう。
var xhr = new XMLHttpRequest();
xhr.open("GET", "sample.html", true);
xhr.send();
xhr.onreadystatechange = function() {
if (this.readyState == 4 && this.status == 200) {
// 処理
}
};
通信のコネクションを張ったのち、リクエストを送ります。
レスポンスが返ってくるとコールバック関数が呼ばれます。そのタイミングでHTTPのステータスコードを見て通信の結果をハンドリングします。
jQuery.ajax
jQuery.ajaxは内部的にはXMLHttpRequestを扱っています。素のXHRに比べると非常に直感的に書けるので便利です。
get
$.ajax({
url: ’/users’
})
.done(function() {
// status code 200
console.log(‘success’);
})
.fail(function() {
// status code 200以外
console.log(‘error’);
})
.always(function() {
// 通信後、常に呼ばれる
console.log(‘complete’);
});
post
$.ajax({
method: ‘POST’,
url: ‘/users’,
data: { name: ‘John’ }
})
.done(function(result) {
console.log(result);
});
詳しくは公式リファレンスをご参考ください。
axios
Ajaxライブラリとしてaxiosも人気があります。
Vue.jsの公式ページでもaxiosが紹介されています。
Axiosはクライアントサイドでもサーバサイドでも動作できる優れものです。
クライアントサイドではXHR、サーバサイドではNode.jsのhttpリクエストが動作します。
クライアントとサーバどちらも書き方は共通なので、ユニバーサルなアプリケーションを作る際に向いていると言えます。
実装例を見てみましょう。
get
axios.get(‘/users’, {
params: {
Id: 12345
}
})
.then(function(res) {
console.log(res.data);
})
.catch(function(err) {
console.log(err);
});
post
axios.post('/users’, {
name: ‘John’
})
.then(function(res) {
console.log(res.data);
})
.catch(function(err) {
console.log(err);
});
このようにかなり簡潔に書くことができます。
AxiosはPromiseを返すので、非同期通信後の処理もしやすいです。
jQuery.ajaxとの使い分けとしては、ランディングページなどjQueryベースですでに作られている場合はそのままjQuery.ajaxを使えば良いと思います。
サイト内のどこにもjQueryを使っていない場合や、シングルページアプリケーションなど複雑なアプリケーションを作る場合はaxiosが向いているでしょう。
Fetch API
Fetch APIは非同期通信のための新たに策定されたAPIです。IE以外の主要ブラウザでは動作します。
(IEでも動作させるためにPolyfillがあるので、心配しなくても大丈夫です)
実装例をいくつか挙げてみます。
get
fetch(‘/users’, {
headers: {
‘Content-Type’: ‘application/json’
}
})
.then(function(res) {
return res.json();
})
.then(function(res) {
console.log(res);
});
post
fetch(‘/users’, {
method: ‘POST’,
headers: {
‘Content-Type’: ‘application/json’
},
body: JSON.stringify({
name: ‘John’
}),
})
.then(function(res) {
return res.json();
})
.then(function(res) {
console.log(res);
});
第二引数にはcorsやcredentialsなど、細かい設定が可能です。
詳しくはMDNのドキュメントを参考にしてください。
https://developer.mozilla.org/ja/docs/Web/API/Fetch_API/Using_Fetch
クロスオリジンの罠
ブラウザから同オリジン以外のサーバーに対して通信を行うことはできません。
これを回避するためにはJSONPという形式でリクエストを送るか、CORS設定を行うかの2通りがあります。
前者のJSONPはセキュリティ観点からお勧めできません。
ここではCORS設定について説明します。
CORS(Cross-Origin Resource Sharing)とは
簡単に言うと、別オリジンのサーバーに対する通信を許可する仕組みです。
そのためにはリクエストを受ける側のサーバーにあらかじめ設定が必要となります。
具体的には、HTTPレスポンスの`Access-Control-Allow-Origin`ヘッダーでリクエストを許可するドメインを指定してあげれば良いです。
例えば、`http://foo.example`からのアクセスのみ許可したい場合は、以下のようなレスポンスヘッダーが必要です。
Access-Control-Allow-Origin: http://foo.example
また、すべての通信を許可したい場合は以下のように記述することもできます。
Access-Control-Allow-Origin: *
例えば、WebサーバーとしてExpressを使用している場合、次のように設定します。
app.use((req, res, next) => {
res.header('Access-Control-Allow-Origin', '*');
res.header('Access-Control-Allow-Headers', 'Origin, X-Requested-With, Content-Type, Accept');
res.header('Access-Control-Allow-Methods', 'GET, POST, PUT, DELETE, OPTIONS');
next();
});
これに対し、ブラウザからFetch APIでCORSのリクエストを送りたい場合は次のように記述します。
fetch(‘/users’, {
mode: ‘cors’,
});
まとめ
非同期通信を行う方法としては、XMLHttpRequest(XHR)とFetch APIの2通りがあります。
jQuery.ajaxやaxiosはXHRベース、Fetch APIは新しく策定されたAPIです。(と言ってもすでに結構前ですが・・・)
今後はおそらくFetch APIが主流になっていくでしょう。