我必须将一个函数传递给另一个函数,并将其作为回调执行。问题是有时这个函数是异步的,比如:
async function() {
// Some async actions
}
所以我想执行await callback()
或callback()
取决于它接收的功能类型。
有没有办法知道函数的类型?
我必须将一个函数传递给另一个函数,并将其作为回调执行。问题是有时这个函数是异步的,比如:
async function() {
// Some async actions
}
所以我想执行await callback()
或callback()
取决于它接收的功能类型。
有没有办法知道函数的类型?
本机async
函数在转换为字符串时可能是可识别的:
asyncFn[Symbol.toStringTag] === 'AsyncFunction'
或者通过AsyncFunction
构造函数:
const AsyncFunction = (async () => {}).constructor;
asyncFn instanceof AsyncFunction === true
这不适用于 Babel/TypeScript 输出,因为asyncFn
它是转译代码中的常规函数,它是Function
or的实例GeneratorFunction
,而不是AsyncFunction
。为了确保它不会对转译代码中的生成器和常规函数产生误报:
const AsyncFunction = (async () => {}).constructor;
const GeneratorFunction = (function* () => {}).constructor;
(asyncFn instanceof AsyncFunction && AsyncFunction !== Function && AsyncFunction !== GeneratorFunction) === true
由于原生async
函数是在 2017 年正式引入 Node.js 的,所以问题很可能是指async
函数的Babel 实现,它依赖于transform-async-to-generator
转换async
为生成器函数,也可能用于transform-regenerator
将生成器转换为常规函数。
async
函数调用的结果是一个promise。根据提案,可以将Promise或非Promise传递给await
,因此await callback()
是通用的。
可能需要这样做的边缘情况很少。例如,本机async
函数在内部使用本机Promise,Promise
如果其实现发生更改,则不会选择全局:
let NativePromise = Promise;
Promise = CustomPromiseImplementation;
Promise.resolve() instanceof Promise === true
(async () => {})() instanceof Promise === false;
(async () => {})() instanceof NativePromise === true;
这可能会影响函数行为(这是Angular 和 Zone.js Promise实现的已知问题)。即便如此,最好检测函数返回值不是预期的Promise
实例,而不是检测函数是async
,因为同样的问题适用于任何使用替代Promise实现的函数,而不仅仅是async
(所述 Angular 问题的解决方案是包装async
返回值Promise.resolve
)。
从外部看,async
function 只是一个无条件返回原生 promise 的函数,因此它应该被视为一个函数。即使一个函数曾经被定义过async
,它也可以在某个时候被转译并成为常规函数。
在 ES6 中,一个可能返回Promise的函数可以与Promise.resolve
(允许同步错误)或包装Promise
构造函数(处理同步错误)一起使用:
Promise.resolve(fnThatPossiblyReturnsAPromise())
.then(result => ...);
new Promise(resolve => resolve(fnThatPossiblyReturnsAPromiseOrThrows()))
.then(result => ...);
在 ES2017 中,这是通过await
(这是问题中的示例应该如何编写)完成的:
let result = await fnThatPossiblyReturnsAPromiseOrThrows();
...
检查一个对象是否是一个 Promise 是一个单独的问题,但通常它不应该太严格或太松散以涵盖极端情况。instanceof Promise
如果 globalPromise
被替换,则可能无法工作, Promise !== (async () => {})().constructor
. 当 Angular 和非 Angular 应用程序交互时,可能会发生这种情况。
一个需要是 的函数async
,即总是返回一个promise应该首先被调用,然后返回值被检查为一个promise:
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:async
函数不应与返回Promise的常规函数区分开来。没有可靠的方法和实际理由来检测非本地转译async
函数。
我更喜欢这种简单的方式:
theFunc.constructor.name == 'AsyncFunction'
@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*
);
}
这是一个非常有效的问题,我很沮丧有人对他投了反对票。此类检查的主要用例是库/框架/装饰器。
现在还处于早期阶段,我们不应该对VALID问题投反对票。
如果您使用的是 NodeJS 10.x 或更高版本
使用本机 util 函数。
util.types.isAsyncFunction(function foo() {}); // Returns false
util.types.isAsyncFunction(async function foo() {}); // Returns true
但是,请记住上面的所有问题。一个偶然返回一个 promise 的函数,将返回一个假阴性。
最重要的是(来自文档):
请注意,这只会报告 JavaScript 引擎看到的内容;特别是,如果使用了转译工具,返回值可能与原始源代码不匹配。
但是如果你async
在 NodeJS 10 中使用并且没有翻译。这是一个很好的解决方案。
似乎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()