我必须将一个函数传递给另一个函数,并将其作为回调执行。问题是有时这个函数是异步的,比如:
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 的,所以问题可能是指 Babel 实现的async
函数,它依赖于transform-async-to-generator
转译async
为生成器函数,也可能用于transform-regenerator
将生成器转译为常规函数。
async
函数调用的结果是一个承诺。根据proposal,可以将promise 或non-promise 传递给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
实例而不是检测函数是async
,因为同样的问题适用于任何使用替代承诺实现的函数,而不仅仅是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();
...
检查一个对象是否是一个承诺是一个单独的问题,但通常它不应该太严格或太松以涵盖极端情况。如果 global被替换, instanceof Promise
可能无法正常工作, . 当 Angular 和非 Angular 应用程序接口时,可能会发生这种情况。Promise
Promise !== (async () => {})().constructor
一个需要是的函数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:async
不应将函数与返回承诺的常规函数区分开来。没有可靠的方法也没有实际的理由来检测非本地转译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
不过,请记住以上分析者的所有担忧。一个偶然返回一个承诺的函数,将返回一个假阴性。
最重要的是(来自文档):
请注意,这只报告 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()
这是 David Walsh 在他的博文中提供的一个简短而有用的方法:
const isAsync = myFunction.constructor.name === "AsyncFunction";
干杯!
简短的回答:暴露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
关键字创建了函数,但请谨慎使用,因为当您检查它时,很可能您做错了什么。
您可以在一开始就假设回调是承诺的:
export async function runSyncOrAsync(callback: Function) {
let promisOrValue = callback()
if (promisOrValue instanceof Promise) {
promisOrValue = Promise.resolve(promisOrValue)
}
return promisOrValue;
}
他们在你的代码中你可以这样做:
await runSyncOrAsync(callback)
这将解决您不知道回调类型的问题....
我总是交替使用 Promises 和 async/await,因为它们基本相同。
Async/Await 用于处理异步函数中的 Promise。它基本上是 promise 的语法糖。它只是重新设计代码并使 Promise 更易于阅读和使用的包装器。资料来源:GeeksForGeeks
如果您需要一个辅助函数来确定一个值是否是异步函数,而不调用它,或者如果一个值是一个返回Promise的函数,那么您来对地方了。
在这个例子中,我将介绍三种不同的方法。
此函数可以确定是否使用async
关键字定义了函数。
async function a() {}
const b = async () => {}
function isAsyncFunction(f: unknown): boolean {
return f && f.constructor.name === 'AsyncFunction'
}
此函数可以确定常规函数是否返回Promise。为了评估给定函数是否返回 Promise,我们需要调用该函数并检查返回值。为了避免多次调用同一个函数,如果它是 Promise,我们可以返回上述值,false
如果不是。
function a() { return new Promise(() => {}) }
const b = () => new Promise(() => {})
function isPromiseFunction<T>(fn: any, ...params: any[]): Promise<T> | boolean {
const isFunction = fn && typeof fn === 'function'
const notAsyncFunction = fn.constructor.name !== 'AsyncFunction'
if (isFunction && notAsyncFunction) {
const value = fn(...params) || false
if (value && value.constructor.name === 'Promise') {
return value as Promise<T>
}
}
return false
}
因为AsyncFunction和Promise本质上是相同的,所以我们可以检查它们是否都返回一个 Promise。
function isAsync<T>(fn: any, ...params: any[]): Promise<T> | boolean {
const isFunction = fn && typeof fn === 'function'
if (isFunction) {
const value = fn(...params) || false
if (value && value.constructor.name === 'Promise') {
return value as Promise<T>
}
}
return false
}
异步函数验证起来更快、更清晰,而 Promise 函数需要调用才能进行验证。