28
24

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 3 years have passed since last update.

アロー関数とasync/awaitを使った非同期処理の書き方を整理しよう

Last updated at Posted at 2020-09-06

はじめに

昨今のモダンブラウザ、スマフォではアロー関数や、Acync/Awaitを利用したPromiseの利用は、Babelでトランスパイルする必要がない。
これらのJavaScriptを利用の仕方を個別で調べると古い書き方が混在して、今風に書くにはどう書いたら動くのか混乱した。

自分が知りたかった

  1. アロー関数で関数を定義して、その中でasync/awaitを利用した場合の書き方
  2. async/awaitが入れ子で呼ばれた場合どういう動きをするのか
  3. 非同期実行するもの複数実行してその結果を待つにはどうすればいいか

を調査しサンプルコードを書いてまとめた。

各関数のブラウザの対応

下記をリンクから動作を確認できる。値を変えながら動作を確認すると理解しやすい
DEMO

アロー関数を使ったasync/awaitの書き方

数100msスリープするメソッドの例

fetchメソッドなどで非同期にデータを取得するような例を再現するためダミーの関数として利用する

// sleep するメソッド fetchなどの時間がかり結果をpromiseで返す関数
const sleep = (m) => new Promise( _ => setTimeout(_, m))

// promiseをasync awaitを使ってpromise終了後に文字を出力する
// ランダムに数100ms待つ
const asyncFunc = async (mes) => {
    const waitTime = Math.floor((Math.random() * 100) + 1)
    await sleep(waitTime)
    console.log(mes)
    return `result_${mes}`
}

asyncをメソッドの頭につけて囲い、const asyncFunc = async (mes) => {

非同期実行するメソッドの完了を待つ際にawaitをつける await sleep(waitTime)

このように定義すると、asyncFunc自体実行した結果result_${mes}を得るためにはawaitする必要がある。ここが非常にややこしい。
メソッドの最後にreturnしているだけなのに、その時の返り値は文字列ではなくPromiseオブジェクトなのだ。この仕様はややこしくしている原因だ。

returnの結果を得るためには、呼び出し元でawait asyncFunc("message")awaitを先頭につけてあげる必要がある。

async/awaitが入れ子で呼ばれた場合どういう動きをするのか

下記の例のようにawaitを実行するためにはasyncを使ったparentFuncを作成し、その中で実行する必要がある。
この関数内で基本、基本2、基本3の動作を確認し、どういう値が返ってくるか確認してもらいたい。

逐次実行されるように、この関数の一番最後の行実行時にそれ以前の命令した結果がすべて完了させるには基本3のように書けば良い。

端的に言えばasyncで定義している関数の手前にawaitを書いてあげるだけである。

const parentFunc = async () => {
  // asyncを使っているメソッドはpromiseが返ってくるので、asyncを使って呼び出さないとawitが使えず非同期処理になる
  const async = asyncFunc
  const sync = asyncFunc
  
  // 基本
  console.log(async("async")) // Promiseオブジェクトが返ってくるだけ
  console.log("---1---") // 1
  console.log(await sync("sync")) // 2

  // 基本2
  console.log(await sync("sync2")) // 1
  console.log(async("async2")) // Promiseオブジェクトが返ってくるだけ
  console.log("---2---") // 2

  // 基本3
  const resultSync = await sync("sync3") // 1
  console.log(await sync(resultSync)) // 2 resultSyncの結果を待ってから実行する
  console.log("---3---") // 3
}

parentFunc() //ここで上で定義したparentFuncを実行

無名関数でasyncで定義した関数を実行する

上記のようにparentFuncを定義せずに、下記の例のように無名関数で実行することもできる。
個人的にはわかりにくいカッコをたくさん書く必要があり書き方が直感的ではない。おまじないと考えてもいいかもしれない。こういうのはあまり好きじゃないなぁ

(async () => {
  console.log(await asyncFunc("無名関数をasyncで定義して非同期実行するpromiseのメソッドを動かす"))
})()

非同期実行するもの複数実行してその結果を待つにはどうすればいいか

残念なことにせっかくasync/awaitで隠蔽化したPromiseをコードに書く必要がある。
そのキーワードはPromise.all
感覚的にはスレッドの完了をすべて待つ処理に似ている。

const parentFunc = async () => {
  // asyncを使っているメソッドはpromiseが返ってくるので、asyncを使って呼び出さないとawitが使えず非同期処理になる
  const async = asyncFunc
  const sync = asyncFunc
  
  // map関数内で非同期処理が入る場合
  // idの配列を受け取って各idをパラメタにWeb apiにアクセスするような場合
  const arr = [1, 2, 3, 4, 5]

  // mapの中の各メソッドが実行完了する順番が変わっても結果の配列の順番はarrの順
  const result_arr1 = await Promise.all(arr.map((id)=>{
    return sync(id);
  }))
  console.log(result_arr1)
}

parentFunc()
28
24
0

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
28
24

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?