【JavaScript】JavaScriptまとめ⑨（通信と非同期処理）

# 概要
JavaScriptの理解を深めるため、
[<img width="200" alt="51vlB3JskRL.jpg" src="https://m.media-amazon.com/images/I/41vZUGXaYML._SX350_BO1,204,203,200_.jpg">](https://www.amazon.co.jp/%E3%82%B9%E3%83%A9%E3%82%B9%E3%83%A9%E3%82%8F%E3%81%8B%E3%82%8BJavaScript-%E6%96%B0%E7%89%88-%E6%A1%9C%E5%BA%AD-%E6%B4%8B%E4%B9%8B/dp/4798173266/ref=sr_1_15?crid=341O9294X80Z7&keywords=javascript&qid=1679238835&sprefix=javas%2Caps%2C185&sr=8-15)
で学習した内容を記載していく。

本記事では、通信と非同期処理について記載する。

## 通信と非同期処理
### 効率的なページ遷移
通常のWebサイトは、ページを表示する際に、ブラウザからサーバーへURLをリクエストする。サーバは該当のページを表示するのに必要なHTMLやCSS、JavaScript、画像ファイルなどをブラウザに渡す。そしてブラウザはファイルを受け取って画面にページを描画する。
別のページへ遷移すると、先ほどと同じように必要なファイルを、サーバから取得し、読み直す必要がある。当然、ページ自体がまったく異なるものであれば、それで問題はない。
しかし、大部分は同じで一部分だけが異なるページへ遷移する場合は、すべてのファイルを読み込み直すのはとても非効率である。
そこで、`Ajax`と呼ばれる、JavaScriptでサーバから必要なデータのみを取得し、ページを部分的に書き換える手法がよく使われる。
#### データ形式JSONについて
部分的に必要なデータだけをサーバから取得する場合、どのような形式のデータを受け取れば良いか。本記事では最もよく使われる、`JSON`と呼ばれるデータ形式を使う。
##### JSONの記法例
```
{
    "name" : "tarou",
    "languages" : ["JavaScript", "PHP", "Python"]
}
```
全体を波括弧（`{}`）で囲み、その中にコロン（`:`）で区切られたキーと値の組み合わせを並べるのが基本的な形である。キーと値の組み合わせは、末尾のカンマ（`,`）で区切りがつけられている。
この記法はオブジェクトの書き方とほぼ同じである。違いはキー名はダブルクォーテーション（`"`）で囲む必要があること。
### 非同期処理
#### 同期と非同期
一般的に、プログラムは書いたコードの順序通りに上から下に実行する。一番最初のコード実行が完了するのを待ってから、下の行に移動し次のコードを実行していく。これを`同期処理`と呼ぶ。
しかし`非同期処理`は、コード実行の完了を待たずに次のコードを実行する。
#### 非同期処理がなぜ必要なのか
同期処理では実行したコードの完了を待つため、実行結果を受け取り、変数に格納することができる。
```
// 変数resultに実行結果を格納できる
const result = 同期処理();
```
しかし、非同期処理は実行したコードの完了を待たない。そのため、実行結果をその場で受け取って変数に格納することはできない。
```
// 変数resultには結果は格納されない
const result = 非同期処理();
```
何か時間がかかる処理をしていたとしても、利用者の操作を極力妨げないような工夫がされている。この工夫が、まさに処理の完了を待たない非同期処理である。
それでは同期処理をJavaScriptで再現してみる。下記のコードをデベロッパーツールのコンソールに入力する。
```sync_alert.js
function syncAlert() {
    alert('アラートを表示');
    console.log('ログを出力');
}
syncAlert();
```
アラートが表示され、OKを押して閉じると、コンソールに「ログを出力」と表示される。これは`alert()`が同期処理する機能のため、OKを押してアラートを閉じない限りは後続のコードが実行されず待機している状態ということである。
続いて、非同期処理の流れを再現してみる。
```async_alert.js
function asyncAlert() {
    setTimeout(function () {
        alert('アラートを表示');
    }, 0);
    console.log('ログを出力');
}
asyncAlert();
```
今度は実行するとアラートが表示されたときに、コンソールに「ログを出力」と表示される。
これは`setTimeout()`が非同期処理であるから。`setTimeout()`は実行する内容や、指定した実行秒数にかかわらず、即座に後続のコードを実行する。

### 非同期通信
#### 通信も待機する
通信も同期処理になるとサーバからの返事を受け取ってからでないと後続の処理を実行できない。
そこで通信を非同期処理とすることで、サーバからの応答を待たずに他の処理をすることができる。
#### JSONデータを用意する
まず「15」というフォルダを作る。そして、下記の内容で新しく「sample.json」という名前のファイルを作り、「15」フォルダの中に保存する。
```sample.json
{
    "text": "Hello Javascript!"
}
```
そしてVisual Studio CodeのLive Serverを起動する。
すると、自動でブラウザが起動する。
#### 通信処理を実装する
JavaScriptからサーバに通信し、先ほど用意したJSONデータを取得するように実装してみる。
```fetch.html
<!DOCTYPE html>
<html>
    <head>
        <meta charset="UTF-8">
    </head>
    <body>
        <div class="result"></div>
        <script>
            fetch('http://127.0.0.1:5500/15/sample.json').then
(function (result) {
                return result.json();
            }).then(function (json) {
                const result = document.querySelector('.result');
                result.textContent = json.text;
            });
        </script>
    </body>
</html>
```
HTMLファイル上では「Hello JavaScript!」という文字列は書いていないが、サーバから取得したsample.jsonファイルに書かれている文字列を受け取って表示している。
##### fetch()通信の書き方
`fetch()`でJSONデータを取得するにはfetch()の後に2回の`then()`というメソッドをつなげる。
```
fetch().then().then();
```
fetch()の引数には通信したいURLを指定する。
```
fetch(通信したいURL).then().then();
```
続いて、1個目のthen()メソッドの引数には関数を渡す。関数の役割はサーバからのレスポンスに対してJSONデータを抜き出す処理である。
引数で自動で受け渡されるレスポンスオブジェクトの`responce`が持つ`json()`メソッドを実行して返す。
```
fetch(通信したいURL).then(function (responce) {
    return responce.json();
}).then();
```
2個目のthen()にも同様に関数を渡す。この関数の役割は、1個目で抜き出したJSONデータを受け取って、任意の処理をすること。
```
fetch(通信したいURL).then(function (responce) {
    return responce.json();
}).then(function (json) {
    console.log(json);
});
```
こうしてようやくサーバと通信し、JSONデータを取得することができる。
#### 処理が失敗した場合
サーバ自体が応答不能の可能性や、通信自体が成功してもJSONではないデータが返ってくることもある。このように処理が失敗した際にどうすればよいのかというと、then()の最後に`catch()`メソッドをつなげて、失敗結果を受け取るようにしておく。
```
fetch('/sample').then(function (responce) {
    return responce.json();
}).then(function (json) {
    console.log(json);
}).catch(function (error) {
    // 処理が失敗した場合
    console.log(error);
});
```
catch()はthen()と同じように関数を引数で受け取る。処理が失敗するとこの関数が実行され、エラーの情報を受け取ることができる。

### async/awaitで非同期処理を簡単に書く
最近ブラウザにサポートされた新しい記法、`async/await`を使って、直感的にfetch()を使うことができる方法を紹介する。
使い方は、fetch()やjson()メソッドの前に`await`をつけ、その処理をする全体の関数の前に`async`と宣言するだけである。先ほど作ったJSONを表示するコードをasync/awaitを使って書き換えてみる。
```await.html
<!DOCTYPE html>
<html>
    <head>
        <meta charset="UTF-8">
    </head>
    <body>
        <div class="result"></div>
        <script>
            async function showJsonText() {
                const responce = await fetch('http://127.0.0.1:5500/
15/sample.json');
                const json = await responce.json();
                const resultBox = document.querySelector('.result');
                resultBox.textContent = json.text;
            }
            showJsonText();
        </script>
    </body>
</html>
```
このコードにはthen()が出てこない。代わりにthen()をつけていたメソッドの前にawaitがある。これがthen()と同じ役割を持っていて非同期処理の実行結果を待機するようになる。
後続の`responce.json()`メソッドも同様に非同期処理なのでawaitで待機し、実際のJSONデータを変数に格納して受け取っている。