TL;DR
非同期処理を、同期処理のような見た目で書くための構文。async を付けた関数の中で await を使うと、結果が出るまでその場で処理を中断し、結果が出たら続きから再開する。JavaScript や Rust などにある。
なぜ必要か
JavaScript では、Promise のチェーンを明示的に組まずに、Promise に基づく非同期の振る舞いをすっきり書ける。非同期のコードを普通の try / catch で囲んでエラーを扱える。Effective TypeScript 項目27 でも、Promise を直接使うより async/await を使うよう勧めている。
仕組み
JavaScript の async 関数は、常に Promise を返す。戻り値が Promise でなければ、暗黙に Promise で包まれる。await は、Promise が履行または拒否されるまで実行を中断し、解決された値を式の値にする。最初の await までは同期的に実行される。
Rust では、async fn を呼んでも本体は実行されず、本体と引数を包んだ Future が返る。Future は .await されるまで何もしない。結果がまだ出ていなければ、.await はスケジューラに制御を渡し、別のタスクを進めさせる。Future を動かすランタイムは言語に組み込まれておらず、Tokio などから選ぶ。Async Rust によると、コンパイラは async/await の記述を状態遷移マシンに書き換えるので、スタックを使わない低コストな非同期実行ができる。
例
async function getProcessedData(url) {
let v
try {
v = await downloadData(url)
} catch (e) {
v = await downloadFallbackData(url)
}
return processDataInWorker(v)
}注意点
Async Rust 第1章 によると、スレッドと async/await の違いは、スケジューリングの方式、スケジュールする主体、スタックの有無にある。async/await はノンプリエンプティブで、OS を介さずユーザ空間で切り替える。多くのアプリケーションはスレッドで十分である。Rust で .await を順に並べるだけでは並行にならないので、並行に動かすには tokio::spawn などでタスクを作る。