Promiseに出来てasync/awaitに出来ないこと (JavaScriptを例として)

JavaScriptで非同期処理をasync/awaitを使って同期的なスタイルで書いていると、すべてのコードをそのスタイルで統一して書きたくなる。なので非同期処理を開始して実行を明け渡したいときはもちろんawaitを使うし、非同期処理に失敗したときはtry-catch構文で例外ハンドラに制御が移るようにする。ただ、同期的なスタイルで書けない処理が存在するために、どうしてもすべてを統一することはできない。Direct styleで書けないcontrolは継続渡しスタイル(CPS)を使って書くしかないからだ。

JSの場合でいうと、並行制御周りがそれにあたる。Promise.all()Promise.race() などは対応する構文がJS側に存在しない。 例えば Promise.all() に対応する awaitall みたいな構文が言語側に欲しくなる。こんなふうに:

const [x, y] = awaitall f(), g();

(完全なコード)

const [x, y] = await Promise.all([f(), g()]);

と書けば見栄えとしてはほぼ変わらないが、Promiseは結局のところコールバック関数(明示的継続)のコンビネータにすぎないので、Promiseを使うとCPSで書いていることになる、と自分は思う。

そもそもJSには並行制御の構文は存在しないので、すべてをdirect styleで置き換えようとするのには無理がある。それはこれまでランタイムがコールバックスタイルで並行制御APIを提供してきたためにCPSで書くことを強いられてきたという歴史があるからだ。逆に考えると、並行制御のための構文が存在しなくても並行制御が書けるというの驚くべきことだ。それだけCPSは表現力が高い。

参考