Panda Noir

JavaScript の限界を究めるブログでした。最近は幅広めに書いてます。

asyncキーワードはなぜ生まれたのか?

みなさん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に責務を負担させすぎた」実例があります。この二の舞いになることを避けるために、明示的に宣言するスタイルを取った、というのが事の真相ではないでしょうか。