JavaScriptの非同期処理メモ

# はじめに
JavaScriptの非同期処理メモ

# 参考
https://www.amazon.co.jp/%E3%83%97%E3%83%AD%E3%82%92%E7%9B%AE%E6%8C%87%E3%81%99%E4%BA%BA%E3%81%AE%E3%81%9F%E3%82%81%E3%81%AETypeScript%E5%85%A5%E9%96%80-%E5%AE%89%E5%85%A8%E3%81%AA%E3%82%B3%E3%83%BC%E3%83%89%E3%81%AE%E6%9B%B8%E3%81%8D%E6%96%B9%E3%81%8B%E3%82%89%E9%AB%98%E5%BA%A6%E3%81%AA%E5%9E%8B%E3%81%AE%E4%BD%BF%E3%81%84%E6%96%B9%E3%81%BE%E3%81%A7-Software-Design-plus/dp/4297127474/?_encoding=UTF8&pd_rd_w=SevaS&content-id=amzn1.sym.188d4668-4672-4f36-9ad0-64c3725855d9%3Aamzn1.symc.573c83ff-b207-408b-b0c7-3d92bb6b7d04&pf_rd_p=188d4668-4672-4f36-9ad0-64c3725855d9&pf_rd_r=2GS5RJXP5B17Y35KQVS5&pd_rd_wg=BGlhO&pd_rd_r=de10c02c-f91d-4aaf-bb70-f7816ce93057&ref_=pd_gw_ci_mcx_mr_hp_atf_m

https://logmi.jp/tech/articles/329462

# まとめ
* 前提としてJavaScriptはシングルスレッドのため特定の処理（例えば非同期処理やI/O操作）が完了するのを待たずに次の処理を進める
* だが、非同期処理を複数考える場合、終了の順番によって、結果が常にランダムになる(終わる順番が保証されていない) -> 非同期処理を順番に実行したい
* 非同期処理を順番に実行
  * `コールバックで実現` -> `Promiseのthenで実現`　-> `async/awaitで実現`
* TypeScriptで非同期処理を扱う場合は`async/await`を利用する
* `async/await`は`Promise`のシンタックスシュガーなため、`Promise`は理解する

# 非同期処理とは
* 多くの場合時間がかかる処理で、裏で行われるもの
* 通信処理とかディスクアクセス処理とか

プログラムが他の作業を待たずに次の処理を進めること

# JavaScriptの非同期処理
`JavaScriptの実行環境`が、非同期処理についてはバックグラウンドで処理させて、次の処理に移れるような仕組みを持っている。
例えば、setTimeout関数は非同期処理なので、setTimeoutの処理終了をまたずに次の処理が実行される、など

JavaScript自体はシングルスレッドで動作する言語ですが、それを実行する環境（例: ブラウザやNode.js）は非同期処理をバックグラウンドでサポートする仕組みを提供しているはず

https://logmi.jp/tech/articles/329462

# 非同期処理に順序を付けたい場合
## 非同期処理関数のコールバック関数
* 非同期処理の完了をコールバック関数で検知する仕組み
* 非同期処理を順番に実行する場合、非同期処理にコールバック関数を渡していた。そして、コールバック関数の中に次の非同期処理とそのコールバックをネストする形で記述して順番通りに非同期処理を実行していた。コールバック関数を使わないと、非同期処理が終わった順番で実行されてしまう。
* このコールバック関数の利用方法(コールバック関数の引数の数、型など)が各APIで違っていて、毎回調べる必要があった

しかし、非同期処理を重ねていくとネストがどんどん深くなってコードが見づらくなり、流れも追いにくくなります。これが`コールバック地獄`です。

## Promise
**Promise は「非同期で処理を走らせて、結果があとで手に入る」ってだけの仕組み。**

* Promiseを使った非同期処理の関数は、コールバック関数を引数に取るのではなくPromiseオブジェクトを返します。**Promise は返された瞬間にもう実行されている**

* コールバック関数を使う場合は、API毎にコールバック関数をどのように渡すのか毎回調べる必要があったが、Promiseの使い方さえ覚えれば非同期処理を受け取れるようになった
**非同期処理を扱う使い方がそもそも便利になった↑**
* もちろん、thenをつなげることで簡潔に非同期処理を順番に連鎖させることができるようになった

Promise は、その非同期処理が 完了するまで待機するための仕組み です。具体的には、非同期処理の結果を 後で使うことができる ようにするためのオブジェクトです。

Promise は以下の3つの状態を持っています

* Pending（保留中）: 非同期処理がまだ終わっていない状態

* Fulfilled（完了）: 非同期処理が成功した状態。結果の値を持っている

* Rejected（拒否）: 非同期処理が失敗した状態。エラーメッセージなどの理由を持っている


## Promiseをイメージする
**Promiseは非同期処理そのものを表している**
```typescript
const p = new Promise<number>((resolve) => {
   // setTimeoutは非同期処理関数
   // ライブラリもここに非同期処理が記述されていて、その中でresolve()を利用しているはず
   setTimeout(() => {
      resolve(100);
  }, 3000);
});

p.then((num) => {
  console.log(num);
});
```
* resolveは内部的に用意される関数で、非同期処理が終わったらresolveを呼び出されることを求められている。なのでここでは3秒後にresolve()している
* resolve()が呼び出されると、Promiseが成功に解決される
* 引数の関数は(resolve) =>{...} 即座に実行される
* イメージ
   * 例えばfetchはPromiseを返すが、非同期処理が引数のURLの通信で、それは即座に実行されていて、通信が終わればresolve()を実行していて、Promiseのthenが呼べる状態になっているはず、、

```javascript
function fetchDataFromAPI(url) {
  return new Promise((resolve, reject) => {
    fetch(url)
      .then((response) => {
        if (!response.ok) {
          reject('APIリクエストが失敗しました');  // ステータスコードが 200 以外の場合はエラー
        } else {
          return response.json();  // JSON データをパース
        }
      })
      .then((data) => {
        resolve(data);  // APIからのデータを成功として返す
      })
      .catch((error) => {
        reject(`エラー: ${error.message}`);  // エラーが発生した場合
      });
  });
}

fetchDataFromAPI('https://jsonplaceholder.typicode.com/posts/1')
  .then((result) => {
    console.log('データ取得成功:', result);  // 成功した場合の処理
  })
  .catch((error) => {
    console.error('データ取得失敗:', error);  // 失敗した場合の処理
  });
```

このpromiseを返すのがfetchとか

```typescript
function fetch(url, options) {
  return new Promise((resolve, reject) => {
    const xhr = new XMLHttpRequest()

    xhr.open(options.method || 'GET', url)
    for (const key in options.headers) {
      xhr.setRequestHeader(key, options.headers[key])
    }

    xhr.onload = () => {
      const response = {
        ok: xhr.status >= 200 && xhr.status < 300,
        status: xhr.status,
        json: () => Promise.resolve(JSON.parse(xhr.responseText)),
        text: () => Promise.resolve(xhr.responseText),
      }
      resolve(response)
    }

    xhr.onerror = () => reject(new TypeError('Network request failed'))

    xhr.send(options.body)
  })
}
```

```javascript
Promise<T>
```
将来 T が返ってくる非同期な値

# さらに便利になったasync/await
* 非同期処理をPromiseを意識しないで記述できるようになった。なので非同期処理を書く場合は大抵の場合`async/await`を記述すると思われる
* `async/await`を使うと、より同期的な処理の書き方で非同期処理を順番に記述できるようになった
* エラー処理もtyr-catch処理で記述することができる
* asyncもawaitも`Promise`を返すので、`Promise`を理解していることが大事

* awaitを使うと、asyncの関数が中断される、asyncの関数の次から実行される

```typescript
function exampleFunction = async() => {
     // asyncの中でawaitを書くことで同期的に処理をかけるようになる
    console.log("Start of async function");

    const result = await new Promise((resolve) => {
        setTimeout(() => {
            resolve("Resolved after 2 seconds");
        }, 2000);
    });

    console.log(result);
    console.log("End of async function");
}

console.log("Before calling async function");
exampleFunction();
console.log("After calling async function");
```
表示
```
Before calling async function
Start of async function
After calling async function
Resolved after 2 seconds
End of async function
```