JavaScript Fetch API の解説と使い方

# JavaScript Fetch API について

この記事は、非同期でリクエストを発行し、そのレスポンスを取得するJavaScriptのFetch APIについて解説した上で使い方を簡単に紹介します。

## 非同期処理とは

Fetch APIの最大とも言える特徴は、その非同期性にあります。非同期処理を理解するためには、まず同期処理について理解しなければいけません。

同期（Synchronization）は、プログラミングの文脈において、何かを行う処理を時間的に一致させることを指します。さらに言えば、処理Aを行う際、別の処理Bの完了まで待つことを同期といいます。

同期処理では、待ち合わせする間、他の処理が行えないため、効率が悪くなりがちです。そこで、処理Bの完了を待たずに処理を行うこと、これが非同期処理です。

非同期処理では、ある処理の完了を待たずに（あるいは待っている間に）、他の処理を行えることから、プログラムの効率を高めることができます。

## Fetch APIで見てみる非同期処理

以下は実際に Fetch API を利用して、気象庁ホームページ防災気象情報からjsonを取ってくる非同期処理です。

```javascript=
<script>
    
// 東京(130000)の予報を取得
let url = "https://www.jma.go.jp/bosai/forecast/data/overview_forecast/130000.json";

fetch(url)
   .then(function(response) {
       return response.json();
   })
   .then(function(weather) {
    　　　　　　console.log('こちらの方が後に表示される')
       console.log(weather);
   });

console.log('こちらの方が先に表示される')

</script>
```

ここで特徴的なのは、then()という書き方です。これはJavaScriptのPromiseオブジェクトにおける特徴的な書き方で、

* 前の処理が正常に完了した時に、then()の中身を同期的に実行する。
* それ以外の処理は非同期で（前の処理の完了を待たずに）よろしく実行しておく。

といった処理になります。then()内にコールバック処理を記述するようなイメージです。

## 実際に同期処理と実行時間を比較してみる

例えば、関東地方の各都府県の天気を取ってみるような処理を書いてみます。

まず同期処理ですが、これはXMLHttpRequest()メソッドを使用した逐次処理（として以下のように実装できます。

```javascript=
const startTime = performance.now(); // 開始時間を計測しておく

let request = new XMLHttpRequest();
locations = [
  '080000.json',  // 茨城県
  '090000.json',  // 栃木県
  '100000.json',  // 群馬県
  '110000.json',  // 埼玉県
  '120000.json',  // 千葉県
  '130000.json',  // 東京都
  '140000.json',  // 神奈川県
]
for (var i=0; i<locations.length; ++i) {
  // 実行完了を毎回待ち合わせするため、各県ごとに逐次実行される
  var url = "https://www.jma.go.jp/bosai/forecast/data/overview_forecast/" + locations[i]

  request.open('GET', url, false);
  request.send(null);
  if (request.status == 200){
    let data = request.responseText;
    console.log(JSON.parse(data).text);
  }
}

const endTime = performance.now(); // 終了時間を計測
console.log(endTime - startTime);　// 284.5 ms

</script>
```

284.5ミリ秒かかっていますね。
これを非同期処理にすると大幅に時間が短縮されます。

```javascript=
const startTime = performance.now(); // 開始時間を計測しておく

let request = new XMLHttpRequest();
locations = [
  '080000.json',  // 茨城県
  '090000.json',  // 栃木県
  '100000.json',  // 群馬県
  '110000.json',  // 埼玉県
  '120000.json',  // 千葉県
  '130000.json',  // 東京都
  '140000.json',  // 神奈川県
]
for (var i=0; i<locations.length; ++i) {
  // 実行完了を毎回待ち合わせせず、各県ごとに非同期に実行される
  var url = "https://www.jma.go.jp/bosai/forecast/data/overview_forecast/" + locations[i]
  fetch(url)
     .then(function(response) {
         return response.json();
     })
     .then(function(weather) {
         console.log(weather.text);
     });
}

const endTime = performance.now(); // 終了時間を計測
console.log(endTime - startTime);　 // 4.6 ms

</script>
```

4.6ミリ秒で実行できました。...あれ、本当に ？

非同期処理のプログラムの方は、実は終了時間を計測している時点で、Fetchが全て完了している保証がありません。なので、これをプログラムの実行時間として計測するのはちょっとずるいですね。

Fetch APIが返すのはPromiseオブジェクトなので、こういった場合、Promise.allメソッドを使うとよいでしょう。Promise.allメソッドは、複数の非同期処理の全ての実行完了を保証してくれます。

```javascript=

const startTime = performance.now(); // 開始時間を計測しておく

var locations = [
  '080000.json',  // 茨城県
  '090000.json',  // 栃木県
  '100000.json',  // 群馬県
  '110000.json',  // 埼玉県
  '120000.json',  // 千葉県
  '130000.json',  // 東京都
  '140000.json',  // 神奈川県
]
var promises = []
for (var i=0; i<locations.length; ++i) {
  var url = "https://www.jma.go.jp/bosai/forecast/data/overview_forecast/" + locations[i]
  promises.push(
    fetch(url)
       .then(function(response) {
           return response.json();
       })
       .then(function(weather) {
           console.log(weather.text);
       })
  );
}

// 全都府県の天気取得の処理を待ち合わせ（=同期）してから終了時間を計測
Promise.all(promises)
  .then((result) => {
    const endTime = performance.now(); // 終了時間を計測
    console.log(endTime - startTime);　 // 167.5 ms
  });

</script>
```


うん、167ミリ秒か、妥当ですね、非同期処理により少し効率が上がってます！

## まとめ

* Fetch APIは非同期のWebリクエストをPromiseオブジェクトを使って簡単に記述できる素敵なメソッド

以上、ご訪問ありがとうございました。