JavaScriptがちょっとわかる人向けの、Ajaxの話。

## Ajaxとは？

Ajaxとは、「Asynchronous JavaScript + XML」の略です。
`「Asynchronous = 非同期」`という意味なので、Ajaxは`非同期`な`JavaScript`で`XML`ということですね。

具体的にはこんな感じです。

```json:english.json
[
  {
    "apple": "りんご",
    "car": "くるま",
    "pencil": "えんぴつ"
  }
]
```

```javascript:jQuery
$.ajax({
  url: 'english.json',
  type: 'GET',
  dataType: 'json'
}).done(function(data) {
  console.log(data[0].apple); // りんご
});
```

`XML`となっていますが、HTMLでもJSONでもPHPでも取得することができます。

`english.json`には、簡単な英和辞典（）のデータベースが入っています。
jQueryの`$.ajax`でJSONを取得し、`data`引数に渡しています。

## $.ajaxで返ってくるもの。

`data`引数の中身を見てみると、取得したJSONが格納されています。

```javascript:jQuery
.done(function(data){
  console.log(data);
});
```

この状態だけを見ると、`$.ajax()`は、URLプロパティで指定したファイルだけを取得しているように見えます。
が、実は`data`引数の中身は取得したデータの一部分で、実際にはもっと多くの情報を取得しています。

具体的には、`$.ajax()`の場合、`jqXHR`オブジェクトを取得しています。

`$.ajax().done()`では、`.done()`を第三引数まで指定することができます。

```javascript:jQuery
.done(function(data, textStatus, jqXHR) {
  console.log('success');
  console.log(data);
  console.log(textStatus);
  console.log(jqXHR);
});
```

先程の`data`引数は、第一引数にあたります。
これは、`jqXHR`オブジェクトの`responseJson`プロパティ値と同じものです。

第二引数の`textStatus`は、通信の結果により、`success`, `notmodified`, `nocontent`, `error`, `timeout`, `abort`, `parsererror`のどれかが返ります。

では、`jqXHR`オブジェクトとは、なんでしょうか？

## jqXHRオブジェクトとは？

jQueryの公式ドキュメントを見ると、こう書かれています。

> The jqXHR Object
The jQuery XMLHttpRequest (jqXHR) object returned by $.ajax() as of jQuery 1.5 is a superset of the browser's native XMLHttpRequest object. For example, it contains responseText and responseXML properties, as well as a getResponseHeader() method. When the transport mechanism is something other than XMLHttpRequest (for example, a script tag for a JSONP request) the jqXHR object simulates native XHR functionality where possible.

Google先生の力をかりると、

>jqXHRオブジェクト
jQuery 1.5以降の$ .ajax()によって返されるjQuery XMLHttpRequest（jqXHR）オブジェクトは、ブラウザのネイティブXMLHttpRequestオブジェクトのスーパーセットです。たとえば、responseTextおよびresponseXMLプロパティ、およびgetResponseHeader()メソッドが含まれています。トランスポート・メカニズムがXMLHttpRequest以外のもの（JSONP要求のスクリプト・タグなど）である場合、jqXHRオブジェクトは可能な場合はネイティブXHR機能をシミュレートします。

`ネイティブXMLHttpRequestオブジェクトのスーパーセット`
ここがポイントですね。

`XMLHttpRequest`とは、ネイティブでJavaScriptが用意している、クライアントとサーバー間とをやり取りするためのAPIです。
参照：https://developer.mozilla.org/ja/docs/Web/API/XMLHttpRequest

このAPIを叩くと、`XMLHttpRequest`オブジェクトを取得することができます。

`jqXHR`オブジェクトは、`XMLHttpRequest`オブジェクトをjQueryがより使いやすい形に拡張したものです。

## $.ajaxは、$.Deferredの様なPromiseを返す。

通信が成功した`jqXHR`オブジェクトの`promise`プロパティの中身を見てみると、`resolve`という値があります。
これは、$.Deferredと似たような仕組みで、ajax通信が終わると、Promiseを返します。

ajax通信が始まる前は`pending`という値になっています。
ajax通信が成功すると値が`resolve`に代わり、失敗すると`rejected`に変わります。
`resolve`になると、`done()`が実行され、`rejected`になると`fail()`が実行されます。

```javascript:jQuery
$.ajax({
  url: 'english.json',
  type: 'GET',
  dataType: 'json'
}).done(function(data, textStatus, jqXHR) {
  console.log(data[0].apple); // りんご
}).fail(function(jqXHR, textStatus, errorThrown){
  console.log('通信失敗');
});
```

`.fail()`は、引数が`.done()`と異なります。

ちなみに、`.then()`を使うと、成否の両方を繋げて記述することができます。
また、`.always()`は、成否によって引数の値が変化します。

```javascript:jQuery
$.ajax({
  url: 'english.json',
  type: 'GET',
  dataType: 'json'
}).then(function(data, textStatus, jqXHR) {
  console.log('成功');
}, function(jqXHR, textStatus, errorThrown) {
  console.log('失敗');
}).always(data|jqXHR, textStatus, jqXHR|errorThrown) {
  // 成功すると.done()の引数になり、失敗すると.fail()の引数になる。
});
```

より詳しく知りたい方は、公式ドキュメントをご確認くださいm(_ _)m
http://api.jquery.com/jquery.ajax/

## applyメソッドを$.whenで使用し、順番を保証。

さて、ここからは少し実践的、かつ、ピンポイントなお話をします。

Ajax通信は非同期な処理なので、次の様な処理で、順番を保証したいときは、少し工夫が必要になります。

```01.txt
第一話：プロローグ
```

```02.txt
第二話：君と夏の終わり将来の夢〜（このあと3万行ぐらいリピート）
```

```03.txt
第三話：エピローグ
```

```javascript:jQuery
let index = []; //  ['第一話：プロローグ', '第二話：君と夏の終わり〜', '第三話：エピローグ']の順番で格納したい。

const textFiles = [
  '01.txt',
  '02.txt',
  '03.txt'
];

for (let i = 0; i < textFiles.length; i++) {
  $.ajax({
    url: textFiles[i],
    type: 'GET',
    dataType: 'json'
  }).done(function(data) {
    index.push(data);
  });
}

console.log(index); // ['第一話：プロローグ', '第二話：君と夏の終わり〜', '第三話：エピローグ']とは限らない。
```

このような書き方ですと、Promiseが返ったタイミングで`.done()`が実行されてしまうので、取得に時間がかかると順番が変わってしまいます。

そこで、`$.when.apply()`を使い、順番を保証してあげます。

```javascript:jQuery
let jqXHRs = [];
let index = [];

const textFiles = [
  '01.txt',
  '02.txt',
  '03.txt'
];

for (let i = 0; i < textFiles.length; i++) {
  jqXHRs.push($.ajax({
    url: textFiles[i],
    type: 'GET',
    dataType: 'text'
  }));
}

$.when.apply($, jqXHRs).done(function(){
  for (let i = 0; i < arguments.length; i++) {
    index.push(arguments[i][0]);
  }
  console.log(index); // ['第一話：プロローグ', '第二話：君と夏の終わり〜', '第三話：エピローグ']
});
```

`$.ajax()`で取得した`jqXHR`オブジェクトは、`.push()`された順番に`jqXHRs`配列に格納されます。
ポイントは、`.apply()`を利用し、`$.when()`に可変長引数を渡しているところです。

`.apply()`の第一引数は、`$.when()`関数を呼び出す際に渡す`this`の値です。
今回は、`jqXHR`オブジェクトの親オブジェクトを呼び出しているので、`$`を指定しています。

そして、第二引数で`jqXHRs`配列を指定することにより、すべての`jqXHR`オブジェクトがPromiseを返したタイミングで、`$.when()`は`.done()`を実行しています。

実引数の`arguments`は、`.done()`の仮引数である`data`, `textStatus`, `jqXHR`の３つの引数を、配列のような形で所有しているので、for文で回してあげることにより、順番を保証したまま`data`を取得することができます。

## もし、意味がわからなかったら。。。

ここまでの話の意味がわからなかった方は、オライリーの「[開眼！JavaScript](https://www.oreilly.co.jp/books/9784873116211/)」を一読することをおすすめします。

とても勉強になります。

## ECMAScript2015でXMLHttpRequest

おまけで、`ECMAScript2015`で`XMLHttpRequest`オブジェクトを取得する方法を書いておきます。

```javascript:JavaScript
const _url = 'english.json'; // クロスドメインに注意

const foo = (url) => {
  return new Promise((resolve, reject) => {
    const xhr = new XMLHttpRequest();
    xhr.open('GET', url);
    xhr.onload = () => resolve(xhr.response);
    xhr.onerror = () => reject(xhr.statusText);
    xhr.send();
  });
};

foo(_url).then((data) => {
  const myData = JSON.parse(data); // IE11ではjsonがパースされないため、文字列として受け取りパースする。
  console.log(myData);
}).catch((error) => {
  console.log(error);
});
```

ご参考までに。