我必须将一个函数传递给另一个函数,并将其作为回调执行。问题是有时这个函数是异步的,比如:
async function() {
// Some async actions
}
所以我想根据它接收的函数类型执行await callback()
或callback()
。
有没有办法知道函数的类型?
本机async
函数可能是可识别的when being converted to strings:
asyncFn[Symbol.toStringTag] === 'AsyncFunction'
或者由AsyncFunction
构造函数:
const AsyncFunction = (async () => {}).constructor;
asyncFn instanceof AsyncFunction === true
这不适用于Babel / TypeScript输出,因为asyncFn
在转换代码中是常规函数,它是Function
或GeneratorFunction
的实例,而不是AsyncFunction
。为了确保它不会对转换代码中的生成器和常规函数给出误报:
const AsyncFunction = (async () => {}).constructor;
const GeneratorFunction = (function* () => {}).constructor;
(asyncFn instanceof AsyncFunction && AsyncFunction !== Function && AsyncFunction !== GeneratorFunction) === true
问题显然是指通过async
函数的Babel实现,它依赖于transform-async-to-generator
将async
转换为生成函数,也可以使用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
,因为同样的问题适用于任何使用替代承诺实现的函数,而不仅仅是async
(the solution to said Angular problem是为了包装async
返回值与Promise.resolve
)。
TL; DR:async
函数不应与返回promise的常规函数区分开来。在这种情况下,他们当然不应该被区分。没有可靠的方法也没有理由去检测非原生的async
函数。
@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*
);
}
这是一个非常有效的问题,我很沮丧,有人拒绝投票给他。此类检查的主要用例是库/框架/装饰器。
这些都是早期的,我们不应该低估有效问题。
我更喜欢这种简单的方式:
theFunc.constructor.name == 'AsyncFunction'
如果您使用的是NodeJS 10.x或更高版本
util.types.isAsyncFunction(function foo() {}); // Returns false
util.types.isAsyncFunction(async function foo() {}); // Returns true
从上面的答案中记住所有关注点。一个只是偶然返回一个承诺的函数,将返回一个假阴性。
最重要的是(来自文档):
请注意,这只会报告JavaScript引擎所看到的内容;特别是,如果使用了转换工具,则返回值可能与原始源代码不匹配。
但是如果你在NodeJS 10中使用async
并且没有转换。这是一个很好的解决方案。
简短的回答:在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
关键字创建了一个函数,但请谨慎使用它,因为当您检查它时,很可能您做错了。
似乎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()
您可以在开始时假设回调是承诺:
export async function runSyncOrAsync(callback: Function) {
let promisOrValue = callback()
if (promisOrValue instanceof Promise) {
promisOrValue = Promise.resolve(promisOrValue)
}
return promisOrValue;
}
在你的代码中你可以这样做:
await runSyncOrAsync(callback)
这将解决你的不知道回调类型的问题....
he i,
以下是David Walsh在他的blogpost中提供的方法:
const isAsync = myFunction.constructor.name === "AsyncFunction";
干杯!