如何知道函数是否异步?

问题描述 投票:46回答:8

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

async function() {
 // Some async actions
}

所以我想根据它接收的函数类型执行await callback()callback()

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

javascript node.js async-await ecmascript-next
8个回答
41
投票

本机async函数可能是可识别的when being converted to strings

asyncFn[Symbol.toStringTag] === 'AsyncFunction'

或者由AsyncFunction构造函数:

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

asyncFn instanceof AsyncFunction === true

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

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

(asyncFn instanceof AsyncFunction && AsyncFunction !== Function && AsyncFunction !== GeneratorFunction) === true

问题显然是指通过async函数的Babel实现,它依赖于transform-async-to-generatorasync转换为生成函数,也可以使用transform-regenerator将生成器转换为常规函数。

async函数调用的结果是一个承诺。 According to the proposal,承诺或非承诺可能会传递给await,所以await callback()是普遍的。

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

let NativePromise = Promise;
Promise = CustomPromiseImplementation;

Promise.resolve() instanceof Promise === true
(async () => {})() instanceof Promise === false;
(async () => {})() instanceof NativePromise === true;

这可能会影响功能行为(这是Angular and Zone.js promise implementation的已知问题)。即便如此,最好检测到函数返回值不是预期的Promise实例而不是检测函数是async,因为同样的问题适用于任何使用替代承诺实现的函数,而不仅仅是asyncthe solution to said Angular problem是为了包装async返回值与Promise.resolve)。

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


18
投票

@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*
    );
}

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

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


11
投票

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

theFunc.constructor.name == 'AsyncFunction'

5
投票

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

使用native util function

   util.types.isAsyncFunction(function foo() {});  // Returns false
   util.types.isAsyncFunction(async function foo() {});  // Returns true

从上面的答案中记住所有关注点。一个只是偶然返回一个承诺的函数,将返回一个假阴性。

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

请注意,这只会报告JavaScript引擎所看到的内容;特别是,如果使用了转换工具,则返回值可能与原始源代码不匹配。

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


2
投票

TL; DR

简短的回答:在instaceof exposing之后使用AsyncFunction - 见下文。

答案很长:不要这样做 - 见下文。

怎么做

您可以使用async关键字检测是否声明了函数

创建函数时,它显示它是一个类型函数:

> f1 = function () {};
[Function: f1]

您可以使用instanceof运算符对其进行测试:

> f1 instanceof Function
true

创建异步函数时,它显示它是AsyncFunction类型:

> f2 = async function () {}
[AsyncFunction: f2]

所以可以预期它也可以用instanceof测试:

> f2 instanceof AsyncFunction
ReferenceError: AsyncFunction is not defined

这是为什么?因为AsyncFunction不是全局对象。查看文档:

即使如你所见,它在Reference/Global_Objects下列出...

如果您需要轻松访问AsyncFunction,那么您可以使用我的unexposed模块:

得到一个局部变量:

const { AsyncFunction } = require('unexposed');

或者将全局AsyncFunction与其他全局对象一起添加:

require('unexposed').addGlobals();

现在上面按预期工作:

> f2 = async function () {}
[AsyncFunction: f2]
> f2 instanceof AsyncFunction
true

为什么你不应该这样做

上面的代码将测试函数是否是使用async关键字创建的,但请记住,真正重要的不是函数是如何创建的,而是函数是否返回一个promise。

您可以在任何地方使用此“异步”功能:

const f1 = async () => {
  // ...
};

你也可以用这个:

const f2 = () => new Promise((resolve, reject) => {
});

即使它不是使用async关键字创建的,因此也不会与instanceof或其他答案中发布的任何其他方法匹配。

具体来说,考虑一下:

const f1 = async (x) => {
  // ...
};

const f2 = () => f1(123);

f2只是带有硬编码参数的f1,在这里添加async没有多大意义,即使结果在各方面都与f1一样多“异步”。

摘要

因此,可以检查是否使用async关键字创建了一个函数,但请谨慎使用它,因为当您检查它时,很可能您做错了。


2
投票

似乎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()

0
投票

您可以在开始时假设回调是承诺:

export async function runSyncOrAsync(callback: Function) {

  let promisOrValue = callback()
  if (promisOrValue instanceof Promise) {
    promisOrValue = Promise.resolve(promisOrValue)
  }
  return promisOrValue;
}

在你的代码中你可以这样做:

await runSyncOrAsync(callback)

这将解决你的不知道回调类型的问题....


0
投票

he i,

以下是David Walsh在他的blogpost中提供的方法:

const isAsync = myFunction.constructor.name === "AsyncFunction";

干杯!

© www.soinside.com 2019 - 2024. All rights reserved.