JavaScriptのXMLHttpRequestとPromiseの違い

JavaScript

JavaScriptのXMLHttpRequestとPromiseの違いについてまとめました。
Promiseの書き方は次の記事を参考にしてみてください。

XMLHttpRequestとPromiseの違い

JavaScriptでAJAX通信する方法は複数あります。
Promiseのメリットを示すために、AJAXでHTTPリクエストをする古いやり方を記述してみます。

今回はhttps://restcountries.eu/のWeb APIを活用します。
こちらが共通の関数とします。


  const renderCountry = function (data) {
    const html = `
<img class="country__img" src="${data.flag}" />
    <h3 class="country__name">${data.name}</h3>
`; div.insertAdjacentHTML('beforeend', html);

XMLHttpRequestでの書き方(Callback hell)


  const getCountryData = function(country) {
  const getCountryAndNb = function (country) {
    //AJAX call country 1
    const request = new XMLHttpRequest();

    request.open('GET', `https://restcountries.eu/rest/v2/name/${country}`);
    request.send(); //リクエストを送ると、バックグランドでデータをフェッチしてきます。

    //リクエストがロードされた次の処理を行う。
    request.addEventListener('load', function () {

      // データをとると、[{}]と配列の中にひとつのオブジェクトが入った形式になるので、分割代入で配列を取り除きます。
      const [data] = JSON.parse(this.responseText); //this = request

      //Render country 1
      renderCountry(data);

      //bordersという項目が隣接の国の数を表している
      //2つ目の国は、一つの目の国のbordersの情報が必要なため、必ず後に取得される
      // Get Neighbour country
      const [neighbour] = data.borders; //配列になっているので、分割代入でとる

      if (!neighbour) return;
      const request2 = new XMLHttpRequest();

      // https://restcountries.eu/rest/v2/alpha/{code}
      request2.open(
        'GET',
        `https://restcountries.eu/rest/v2/alpha/${neighbour}`
      ); 
      request2.send(); //リクエストを送ると、バックグランドでデータをフェッチしてきます。

      request2.addEventListener('load', function () {
        //alphaは単純に配列ではない。今回はnameではなくalphaというcodeをサーチしていたので分割代入は不要。
        const data2 = JSON.parse(this.responseText);

      //Render country 2

      renderCountry(data2);

      });
    });
  };

  getCountryAndNb('Japan');

このように、XMLHttpRequest()を変数に格納し、それにopen()メソッドでGETなどの取得方法とURLを指定します。
そして、send()メソッドでリクエストを送ると、バックグランドでデータをフェッチしてきます。
リクエストがロードされた次の処理を行うように、addEventListenerでloadイベントを設定します。
addEventListenerは第二引数にコールバック関数を取ります。
this.responseTextはJSONデータのフォーマットで送られてきます。JSONは基本的には大量の文字列の集まりのため、JavaScriptのオブジェクトに変換します。JSON.parse()を使います。

このaddEventListener('load’,コールバック関数)のコールバック関数の中にさらにaddEventListener('load’,コールバック関数)を入れてAJAXの順番を制御しています。このこと何層にもネストになるコールバック関数をCallback hellといいます。

Promiseでの書き方(Flat Promise chain)


const request3 = fetch('https://...url');
console.log(request3); //Promise
const getCountryData = function (country) {
  fetch(`https://restcountries.eu/rest/v2/name/${country}`)
 .then(function (
    response
  ) {
    console.log(response);
    return response.json();
  }).then(function(data) {
    console.log(data);
    renderCountry(data[0]);
  })
};
getCountryData('Japan');

Promiseは、バックグランドでデータをフェッチするのに、XMLHttpRequestが三行必要なところを一行で行っています。


//XMLHttpRequestの場合3行
const request = new XMLHttpRequest();
request.open('GET', `https://restcountries.eu/rest/v2/name/${country}`); //ここにURLをいれる
request.send(); //リクエストを送ると、バックグランドでデータをフェッチしてきます。

//Promiseの場合1行
const request3 = fetch('https://restcountries.eu/rest/v2/name/japan');

fetch(https://restcountries.eu/rest/v2/name/${country})というのが、Promiseを返します。その返されたPromiseに、then()メソッドをチェーンして次の処理を記述することができます。then()メソッドの中にはコールバック関数を入れます。fulfilledの状態になったらどうしたいのかの処理を書きます。
たとえば、fetchされた上の中身を見ると、body: ReadableStreamという記述があります。これは、JSONでないと中身がみれません。
json()メソッドは、fetch()メソッドで返されたすべてのオブジェクト、結果の値で利用可能です。
json()メソッドも非同期の関数です。つまり、新しいpromiseを返します。そのため、returnする必要があります。
新しいpromiseというのは、次の部分がすべて新しいpromiseになります。


.then(function (
  response
) {
  console.log(response);
  return response.json();
})

そのため、その新しいpromiseにさらにthen()メソッドでチェーンします。そして、データをコンソールに表示します。

このpromiseのチェーンはどれだけあってもつなげることができ、コールバック関数と違って、くの字にどんどんネストが深くなっていかないことから、Flat Promise chainなどと呼ばれています。

 

アロー関数でさらに簡潔に

ちなみに上のコードは、アロー関数を使って簡潔にかくと、次のようになります。


const request3 = fetch('https://restcountries.eu/rest/v2/name/japan');
console.log(request3); //Promise {}
const getCountryData = function (country) {
  fetch(`https://restcountries.eu/rest/v2/name/${country}`)
  .then( response => response.json())
  .then(data => renderCountry(data[0]));
};
getCountryData('Japan');