問題
以下の関数isAsync
をどう実装すればよいか。
async function a() {
return "a";
}
function b() {
return "b";
}
function isAsync(func) {
// もしfuncがasyncならtrueを返す
// そうでないならfalseを返す
}
console.log(isAsync(a)); // => true
console.log(isAsync(b)); // => false
回答
function isAsync(func) {
return func.constructor.name === "AsyncFunction";
}
詳細は言語仕様を参照。
上の実装では不安な人のために、より神経質な回答も用意してある。
function isAsync(func) {
return Object.getPrototypeOf(func) === Object.getPrototypeOf(async function () {});
}
注意点
ある関数が非同期関数か判定するものではないことに注意してほしい。
あくまで、ある関数が**async
として定義されたか判定するものである**。
例えばPromise
を返す関数は、実用的には、async
として定義された関数同様に非同期関数として見なされるものであるが、isAsync
に入れるとfalse
が返ってくる。
function c() {
return Promise.resolve("c")
}
console.log(isAsync(c)); // => false
より実用的には、ある関数がPromise
を返すか判定することができればよいのかもしれない。
しかし、それも以下のような関数をどう判定すればよいのかという問題が残る。
function wtf() {
if (Math.floor(Math.random() * 2) === 0) {
return Promise.resolve("I'm an asynchronous function!");
} else {
return "I'm a synchronous function!";
}
}
ある関数が非同期関数かどうかというのは、多分に人間の主観が混ざってくる話で、もし何らかの客観的な基準を設けられたとしても、その基準を判定することは原理的に不可能か少なくとも簡単なコードでは不可能であることが多いだろう。
その中でも、「async
として定義されているかどうか」という基準は、実用的に微妙ではあるが、簡単なコードで判定が可能な例である。
ES2018に関する補足
ES2018に入ると目されているasync generator functionもisAsync
でtrue
が返ってくるようにしたいのであれば、isAsync
のコードを少し修正する必要がある。
async function* d() {
return "d";
}
function isAsync(func) {
return func.constructor.name === "AsyncFunction"
|| func.constructor.name === "AsyncGeneratorFunction";
}
console.log(isAsync(d)); // => true
詳細はDraftを参照。
参考リンク
Programmatically check if function is async · Issue #78 · tc39/ecmascript-asyncawait