LoginSignup
2
2

More than 1 year has passed since last update.

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

Last updated at Posted at 2021-09-02

はじめに

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

まとめ

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

非同期処理とは

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

非同期処理は一長一短

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

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

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

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が順番通りに表示される

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で制御する

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)
  }
}
2
2
1

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
2
2