如何知道函数是否异步?

Fac*_*teo 58 javascript node.js async-await ecmascript-next

我必须将一个函数传递给另一个函数,并将其作为回调执行.问题是有时这个函数是异步的,比如:

async function() {
 // Some async actions
}
Run Code Online (Sandbox Code Playgroud)

所以我想执行await callback()callback()取决于它接收的函数类型.

有没有办法知道函数的类型?

Est*_*ask 46

转换为字符串时,本async函数可能是可识别的:

asyncFn[Symbol.toStringTag] === 'AsyncFunction'
Run Code Online (Sandbox Code Playgroud)

或者由AsyncFunction构造函数:

const AsyncFunction = (async () => {}).constructor;

asyncFn instanceof AsyncFunction === true
Run Code Online (Sandbox Code Playgroud)

这不适用于Babel/TypeScript输出,因为asyncFn在转换代码中是常规函数,它是Functionor 的实例GeneratorFunction,而不是AsyncFunction.为了确保它不会对转换代码中的生成器和常规函数给出误报:

const AsyncFunction = (async () => {}).constructor;
const GeneratorFunction = (function* () => {}).constructor;

(asyncFn instanceof AsyncFunction && AsyncFunction !== Function && AsyncFunction !== GeneratorFunction) === true
Run Code Online (Sandbox Code Playgroud)

问题显然是指Babel实现的async功能,它依赖于transform-async-to-generator转换async为生成器功能,也可以用于transform-regenerator将生成器转换为常规功能.

async函数调用的结果是一个承诺.根据该提议,可以传递承诺或不承诺await,因此await callback()是普遍的.

只有少数边缘情况可能需要.例如,本机async函数在内部使用本机promise,Promise如果其实现发生更改,则不会获取全局:

let NativePromise = Promise;
Promise = CustomPromiseImplementation;

Promise.resolve() instanceof Promise === true
(async () => {})() instanceof Promise === false;
(async () => {})() instanceof NativePromise === true;
Run Code Online (Sandbox Code Playgroud)

这可能会影响函数行为(这是Angular和Zone.js承诺实现的已知问题).即便如此,最好检测函数返回值不是预期的Promise实例而不是检测函数async,因为同样的问题适用于使用替代promise实现的任何函数,而不仅仅是async(所述Angular问题的解决方案是包装async返回价值与Promise.resolve).

TL; DR:async函数不应与返回promise的常规函数​​区分开来.在这种情况下,他们当然不应该被区分.没有可靠的方法也没有理由去检测非原生的转换async函数.


Cha*_*ira 19

@rnd和@estus都是正确的.

但是,在这里用一个实际的工作解决方案来回答这个问题

function isAsync (func) {
    const string = func.toString().trim();

    return !!(
        // native
        string.match(/^async /) ||
        // babel (this may change, but hey...)
        string.match(/return _ref[^\.]*\.apply/)
        // insert your other dirty transpiler check

        // there are other more complex situations that maybe require you to check the return line for a *promise*
    );
}
Run Code Online (Sandbox Code Playgroud)

这是一个非常有效的问题,我很沮丧,有人拒绝投票给他.此类检查的主要用例是库/框架/装饰器.

这些都是早期的,我们不应该低估有效问题.

  • 请记住,仅仅因为您不知道任何实际场景,并不意味着[没有](https://github.com/pomax/socketless)。所以这是一个需要学习的新东西:您可以发现一个函数是否是异步的,这意味着您可以编写代码来使用或异步函数“做事”,同时保留常规函数(反之亦然)。对于普通代码有用吗?不,我也想不出您需要它的场景。但这对于代码分析、AST 构建器或本身用 JS 编写的转译器来说重要吗?是的:实际上非常重要。 (2认同)

Ale*_*der 16

我更喜欢这种简单的方式:

theFunc.constructor.name == 'AsyncFunction'
Run Code Online (Sandbox Code Playgroud)

  • 当然@EstusFlask,你是完全正确的。如果这是您的情况 - 您需要一个更复杂的解决方案。但在“现实世界”(不是超级特殊或人为的情况) - 人们可以使用这种解决方案,而不是过度杀死怪物检查器。但大家应该知道你在说什么,谢谢你的评论! (3认同)

gyo*_*gyo 14

似乎await也可以用于正常功能。我不确定这是否可以被视为“良好实践”,但它是:

async function asyncFn() {
  // await for some async stuff
  return 'hello from asyncFn' 
}

function syncFn() {
  return 'hello from syncFn'
}

async function run() {
  console.log(await asyncFn()) // 'hello from asyncFn'
  console.log(await syncFn()) // 'hello from syncFn'
}

run()
Run Code Online (Sandbox Code Playgroud)


the*_*gie 8

以下是 David Walsh 在他的博文中提供的一个简短而有用的方法:

const isAsync = myFunction.constructor.name === "AsyncFunction";
Run Code Online (Sandbox Code Playgroud)

干杯!


Ian*_*ers 6

如果您使用的是NodeJS 10.x或更高版本

使用本机util函数

   util.types.isAsyncFunction(function foo() {});  // Returns false
   util.types.isAsyncFunction(async function foo() {});  // Returns true
Run Code Online (Sandbox Code Playgroud)

不过,请不要忘记回答所有问题。刚偶然返回一个承诺的函数将返回一个假负数。

最重要的是(来自文档):

请注意,这只会报告JavaScript引擎正在显示的内容。特别是,如果使用了编译工具,则返回值可能与原始源代码不匹配。

但是,如果您async在NodeJS 10中使用并且没有进行转换。这是一个很好的解决方案。