我必须将一个函数传递给另一个函数,并将其作为回调执行。问题是有时此函数是异步的,例如:
async function() {
// Some async actions
}
因此,我想执行await callback()
或callback()
取决于它所接收的函数的类型。
有没有办法知道函数的类型?
我必须将一个函数传递给另一个函数,并将其作为回调执行。问题是有时此函数是异步的,例如:
async function() {
// Some async actions
}
因此,我想执行await callback()
或callback()
取决于它所接收的函数的类型。
有没有办法知道函数的类型?
Answers:
转换为字符串时,本机async
函数可能是可识别的:
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
函数于2017年正式引入Node.js,因此问题可能涉及Babelasync
函数的实现,该实现依赖于transform-async-to-generator
转换async
为生成器函数,也可能用于transform-regenerator
将生成器转换为常规函数。
async
函数调用的结果是一个承诺。根据该建议,可以将承诺或不承诺传递给await
,这await callback()
是普遍的。
只有少数情况可能需要这样做。例如,本机async
函数在内部使用本机承诺,并且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
return值Promise.resolve
)。
从外部看,async
函数只是一个无条件返回本机承诺的函数,因此应将其视为一个。即使一次定义了功能async
,也可以在某个时候对其进行转换并成为常规函数。
在ES6中,可以将可能返回promise的函数与Promise.resolve
(使同步错误)或包装的Promise
构造函数(处理同步错误)一起使用:
Promise.resolve(fnThatPossiblyReturnsAPromise())
.then(result => ...);
new Promise(resolve => resolve(fnThatPossiblyReturnsAPromiseOrThrows()))
.then(result => ...);
在ES2017中,这是通过以下方式完成的await
(这是应该编写问题中的示例的方式):
let result = await fnThatPossiblyReturnsAPromiseOrThrows();
...
检查对象是否是一个承诺是一个单独的问题,但是通常为了覆盖极端情况,它不应过于严格或过于宽松。instanceof Promise
如果Promise
替换了global可能无法正常工作, Promise !== (async () => {})().constructor
。当Angular和非Angular应用程序接口时,可能会发生这种情况。
首先需要调用必须为be的函数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
不应将函数与返回诺言的常规函数区分开。没有可靠的方法,也没有实际的理由来检测非本机转换async
功能。
AsyncFunction !== Function
检查可以避免误报。在转译的代码中不会有真正的肯定,因为async
函数与转译的代码中的常规函数没有区别。
await
,它适用于承诺和非承诺。这就是答案中的最后一个片段。
我喜欢这种简单的方法:
theFunc.constructor.name == 'AsyncFunction'
theFunc = new class AsyncFunction extends Function {}
。但transpiledasync
功能没有,theFunc = () => __awaiter(void 0, void 0, void 0, function* () { })
。
=== 'AsyncFunction'
@theVoogie的建议使用?
@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*
);
}
这是一个非常有效的问题,令我沮丧的是有人不赞成他。这种检查的主要用例是用于库/框架/装饰器。
这些是早期,我们不应该投票 有效的问题。
_ref
不会存在。
fn.constructor.name
,这将AsyncFunction
用于异步功能。
await
语义的不正确理解所致。函数是否async
处于我所知道的任何实际场景中都没有关系。async
只是一个无条件返回本机Promise的函数-应该被视为一个函数。async
可能在某个时候被转译,这不应该破坏应用程序。对于您所描述的场景,包装器调用一个函数并断言一个值是promise是正确的,而不是断言一个函数为is是正确的async
。如果需要,以防止无效处理程序尽快,这已经在设计时使用TS /流被强制执行
如果您使用的是NodeJS 10.x或更高版本
使用本机util函数。
util.types.isAsyncFunction(function foo() {}); // Returns false
util.types.isAsyncFunction(async function foo() {}); // Returns true
不过,请不要忘记回答所有这些问题。偶然偶然返回一个承诺的函数将返回假负数。
最重要的是(来自文档):
请注意,这只会报告JavaScript引擎正在显示的内容。特别是,如果使用了编译工具,则返回值可能与原始源代码不匹配。
但是,如果您async
在NodeJS 10中使用并且没有进行转换。这是一个很好的解决方案。
简短答案:暴露instaceof
后使用 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
关键字创建了函数,但请谨慎使用,因为您在检查该函数时很可能在做错事。
async
以了解该函数是否在内部执行了异步/等待操作,但未返回任何内容。
then().catch()
异步函数,try/await
而是异步函数。所以是的,你完全应该检查函数的类型,如果你leitimately需要知道它是否是异步或没有,但不能使用instanceof
:使用fn.constructor.name
代替。如果AsyncFunction
不是Function
,则说明它是一个异步函数。
似乎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)
这将在不知道回调类型的情况下解决您的问题。
await
不承诺,它将自动将其包装)