Skip to content
Latchkey

Async/Await とは?非同期コードをきれいに書く

async/await は、ノンブロッキングで非同期のコードを、通常の逐次コードのようにほぼ読める直線的なスタイルで書けるようにする構文です。

非同期コードは、プログラムが遅い操作(ネットワーク呼び出し、ディスク読み取り)を開始し、それが終わるまでブロックする代わりに他の作業を続けられるようにします。歴史的にはそれはネストした callback を意味しました。async/await は promise(あるいは future)の上の糖衣構文で、それらの callback を上から下へ読めるコードに平坦化しつつ、内部ではノンブロッキングのままにします。

async/await が解決する問題

それがなければ、非同期の作業は callback や連鎖した promise を意味し、ロジックが増えるにつれてネストして絡まります。async/await は結果を同期的であるかのように await させ、その間 runtime は結果が準備できるまで thread を解放して他の作業をさせます。

内部の仕組み

async 関数は promise を返します。await に達すると中断し、制御を event loop に戻します。await した promise が解決すると、関数は中断したところから再開します。待機の間、どの thread もブロックされません。

よくある間違い

  • promise の await を忘れ、準備できる前にコードが動く。
  • 呼び出しを並行に実行できるのに、ループ内で await する。
  • reject された promise を処理せず、未処理のエラーにつながる。
  • 非同期コードを並列と思い込む。1 つの thread ではそれは交錯です。

async は並列と同じではない

Node.js のようなシングルスレッドの runtime では、async/await は作業を交錯させます。JavaScript を並列に動かすわけではありません。I/O バウンドの作業(ネットワーク待ち)で輝きますが、CPU バウンドの計算を速くはしません。

簡単な例

const data = await fetch(url); と書くとブロッキング呼び出しのように読めますが、runtime はネットワークリクエストの実行中に他の作業をさばける状態で、レスポンスが届いたときにこの行を再開します。

CI における async/await

flaky なテストの頻繁な原因は await の書き忘れです。テストが非同期の作業の完了前に assert し、タイミングで pass したり fail したりします。これらの race は混雑した runner で間欠的に fail します。Latchkey は欠けた await を直す間、一時的な失敗を自動 retry できるため、1 回の flaky な実行が merge をブロックしません。

重要なポイント

  • async/await はノンブロッキングなコードを、読みやすい逐次的なスタイルで書きます。
  • async 関数は await で中断し、await した promise が解決したときに再開します。
  • I/O には最適ですが parallelism ではありません。await の書き忘れは flaky なテストを招きます。

関連ガイド