みなさんasync/await使っていますか?とても便利な機能ですよね。しかし、ふとこんな疑問をいだきませんか?「これ、なんでわざわざasync関数を導入したんだろう」と。
そうです。awaitさえ実装していれば、機能的に既存の関数で十分に対応ができたはずなのです。一体どうしてasync関数が導入されたのでしょうか?今回はそこを考えてみました。
async関数と関数の違い
まず、async関数と関数の違いについて調査しました。
const hoge = () => 'hoge'; const fuga = async () => 'fuga'; console.log(hoge()); console.log(fuga());
結果は以下のとおりです(Node v10.11.0)
hoge Promise { 'fuga' }
つまりfuga
は() => Promise.resolve('fuga')
と同値ということです。
ではもう少し突っ込んで調査してみます。
const timer = async time => new Promise(resolve => setTimeout(() => resolve(`${time} ms passed`), time)); console.log(timer(100));
Promise { <pending> }
「まだresolveされてないよ」ということです。このように、async関数は返り値をPromiseでラップして返します。
逆に言えば「returnしなければasync関数と通常の関数に違いはない」、つまり「ただの関数とasync関数を分ける必要性はない」のです。ではなぜasync関数が生まれたのでしょうか?
実はgeneratorと関数も区別しなくていい
実は、async関数だけでなくgeneratorも関数とわける必要はありません。「関数内でyieldしたらジェネレータとして扱う」とすればいいのですから。
実際にPHPでは、generatorかどうかを「yieldするかどうか」で判定しています。
しかし、この方法では明示的にジェネレータであると示すことができません。
また、JavaScriptには「functionに責務を負担させすぎた」実例があります。この二の舞いになることを避けるために、明示的に宣言するスタイルを取った、というのが事の真相ではないでしょうか。