如何知道函数是否异步?理论实践

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

async function() {
 // Some async actions
}

因此,我想执行或取决于它正在接收的函数类型。await callback()callback()

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


答案 1

理论

本机函数在转换为字符串时可以识别:async

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

或者通过异步函数构造函数:

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

asyncFn instanceof AsyncFunction === true

这不适用于 Babel/TypeScript 输出,因为它是转译代码中的常规函数,它是 or 的实例,而不是 。要确保它不会在转译代码中为生成器和常规函数提供误报,请执行以下操作:asyncFnFunctionGeneratorFunctionAsyncFunction

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

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

由于原生函数在2017年被正式引入Node.js,这个问题可能是指函数的Babel实现,它依赖于转换-异步-生成器来转译到生成器函数,也可能使用变换-再生器将生成器转译为规则函数。asyncasyncasync

函数调用的结果是一个 promise。根据该提案,承诺或非承诺可以传递给 ,因此是普遍的。asyncawaitawait callback()

只有少数边缘情况可能需要这样做。例如,本机函数在内部使用本机承诺,如果其实现被更改,则不会选择全局函数:asyncPromise

let NativePromise = Promise;
Promise = CustomPromiseImplementation;

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

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

实践

从外部看,函数只是一个无条件返回本机 promise 的函数,因此它应该被视为一个函数。即使一个函数曾经被定义过,它也可以在某个时刻被转译并成为正则函数。asyncasync

可以返回承诺的函数

在ES6中,可能返回承诺的函数可以与(允许同步错误)或包装构造函数(处理同步错误)一起使用:Promise.resolvePromise

Promise.resolve(fnThatPossiblyReturnsAPromise())
.then(result => ...);

new Promise(resolve => resolve(fnThatPossiblyReturnsAPromiseOrThrows()))
.then(result => ...);

在ES2017中,这是通过以下方式完成的(这是问题中的示例应该如何编写的):await

let result = await fnThatPossiblyReturnsAPromiseOrThrows();
...

应返回承诺的函数

检查一个对象是否是一个承诺是一个单独的问题,但通常它不应该太严格或松散,以覆盖角落案例。 如果全局被替换,则可能不起作用。当 Angular 和非 Angular 应用程序接口时,可能会发生这种情况。instanceof PromisePromisePromise !== (async () => {})().constructor

需要的函数,即始终返回 promise 的函数应该首先被调用,然后检查返回的值是一个 promise:async

let promise = fnThatShouldReturnAPromise();
if (promise && typeof promise.then === 'function' && promise[Symbol.toStringTag] === 'Promise') {
  // is compliant native promise implementation
} else {
  throw new Error('async function expected');
}

TL;DR:异步函数不应与返回 promise 的常规函数区分开来。没有可靠的方法,也没有实际理由来检测非本机转译的异步函数。


答案 2

只要只使用本机异步函数(通常是这种情况),我更喜欢这种简单的方法:

theFunc.constructor.name == 'AsyncFunction'