TypeScriptで非同期処理のまとめ　Promiseとasync/await

# はじめに
Typescriptを書いてて、非同期処理にPromiseとasync/awaitを使う２種類の書き方があったのでまとめました。

# まとめ
* 個人的にPromiseだけで書くよりもasync/awaitを使うほうがシンプルに書けると思う

## 非同期処理とは
* 通信が発生する処理で起きる
    * Web　APIを叩く
    * データベースへクエリを投げる
* 実行完了を待たずに次の処理に進む
* Javascriptはシングルスレッドの言語
    *　非同期APIにより効率よく処理を行うことが可能

## 非同期処理は一長一短
* 複数の処理を並行して効率よく実行できる
    * 思い処理や時間のかかる通信中にユーザーに別の操作を許可するなど
* 制御が難しい
    * 処理が実行中なのか実行完了したのかトレースしにくい
* どう対処すべきか
    * ```Promise```や```async/await```で非同期処理を同期的に制御する
    * 型をつけることでわかりやすく！


## 非同期処理をそのままにコードで書く

下記のコードの場合、Asynchronous Callback Sample 2が先に表示されてしまう。。

```typescript
const url = 'https://api.github.com/users/xxxxxx'

// コールバックで呼び出す非同期関数(fetch)
  const fetchProfileCallback = () => {
    return fetch(url)// fetch()：非同期処理となる
      .then((res) => {
        // レスポンスbodyをJSONとして読み取った結果を返す
        res
          .json()
          .then((json: Profile) => {
            console.log('Asynchronous Callback Sample 1:', json)
//Asynchronous Callback Sample 1: {login: ......}
            return json
          })
          .catch((error) => {
            console.error(error)
            return null
          })
      })
      .catch((error) => {
        console.error(error)
        return null
      })
  }

  const profile = fetchProfileCallback()
  // 非同期処理が完了していないのでPromise<pending>が表示される
  console.log('Asynchronous Callback Sample 2:', profile)
//Asynchronous Callback Sample 2: Promise {<pending>}
```

## Promise型で実行完了後の値を定義する
* 非同期処理の実行結果は```Promise<string>```のように定義する
 
### Promiseの状態
* ```Promise<pending>```  : 初期状態/実行中
* ```Promise<fulfilled>```: 処理が成功して完了した状態
* ```Promise<rejected>``` : 処理が失敗して完了した状態

## Promiseで順番を制御する

Promiseを使うと、Asynchronous Promise Sample 1のあと、Asynchronous Promise Sample 2が順番通りに表示される

```typescript:Promiseを使った例
const url = 'https://api.github.com/users/xxxx'

  type Profile = {
    login: string
    id: number
  }

  type FetchProfile = () => Promise<Profile | null> //型を定義

  const fetchProfilePromise: FetchProfile = () => {
    return new Promise((resolve, reject) => {
      fetch(url)
        .then((res) => {
          // レスポンスbodyをJSONとして読み取った結果を返す
          res
            .json()
            .then((json: Profile) => {
              console.log('Asynchronous Promise Sample 1:', json)
              resolve(json) // Promiseを扱う場合、resolve()を使う
            })
            .catch((error) => {
              console.error(error)
              reject(null)
            })
        })
        .catch((error) => {
          console.error(error)
          reject(null)
        })
    })
  }

  fetchProfilePromise().then((profile: Profile | null) => {
    if (profile) {
      console.log('Asynchronous Promise Sample 2:', profile)
    }
  })
```

## async/awaitで制御する

```typescript
export default async function asyncAwaitSample(): Promise<void> { //asyncをつけることができる

  const url = 'https://api.github.com/users/xxxxx'

  type Profile = {
    login: string
    id: number
  }

  type FetchProfile = () => Promise<Profile | null>

  // async/awaitでコールバック関数を同期的な処理に置き換える
  const fetchProfile: FetchProfile = async () => {
    const response = await fetch(url)
      .then((response) => response) //fetch()が成功していたらそのまま返す
      .catch((error) => {
        // 失敗していたら
        console.error(error)
        return null
      })

    // responseがnullならfetchに失敗している
    if (!response) {
      return null
    }

    const json = await response
      .json()
      .then((json: Profile) => {
        console.log('Asynchronous Promise Sample 1:', json)
        return json
      })
      .catch((error) => {
        console.error(error)
        return null
      })

    // jsonがnullならレスポンスBodyの読み取りに失敗している
    if (!json) {
      return null
    }

    return json
  }

  fetchProfile().then((profile: Profile | null) => {
    if (profile) {
      console.log('Asynchronous Promise Sample 2:', profile)
    }
  })

  // さらに同期的な処理にする
  const profile = await fetchProfile()
  if (profile) {
    console.log('Asynchronous Promise Sample 3:', profile)
  }
}
```