我需要将一个函数作为回调传递给另一个函数并执行。问题在于有时这个函数是异步的,例如:
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
asyncFn
在转译代码中是常规函数,它是Function
或GeneratorFunction
的实例,而不是AsyncFunction
。为了确保它不会给转译代码中的生成器和常规函数带来错误的结果:const AsyncFunction = (async () => {}).constructor;
const GeneratorFunction = (function* () => {}).constructor;
(asyncFn instanceof AsyncFunction && AsyncFunction !== Function && AsyncFunction !== GeneratorFunction) === true
async
函数正式引入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;
Promise
实例,而不是检测函数是否为async
,因为同样的问题适用于使用替代promise实现的任何函数,而不仅仅是async
(解决上述Angular问题的方法是将async
返回值包装在Promise.resolve
中)。
从外部来看,async
函数只是一个无条件返回本机promise的函数,因此应该像对待普通函数一样对待它。即使函数曾经被定义为async
,它也可能在某个时刻被转译为常规函数。
Promise.resolve
(处理同步错误)或包装Promise
构造函数(处理同步错误):Promise.resolve(fnThatPossiblyReturnsAPromise())
.then(result => ...);
new Promise(resolve => resolve(fnThatPossiblyReturnsAPromiseOrThrows()))
.then(result => ...);
await
来实现这一点(这就是问题示例应该编写的方式):let result = await fnThatPossiblyReturnsAPromiseOrThrows();
...
检查对象是否为Promise是另一个问题, 但通常不应过于严格或宽松以覆盖边缘情况。如果全局Promise
被替换,instanceof Promise
可能不起作用,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');
}
async
函数不应该与返回 Promise 的普通函数区别对待。没有可靠的方法,也没有实际的理由来检测非原生转译的 async
函数。AsyncFunction !== Function
始终为false。顺便说一下,我正在使用TypeScript。你能否请看一下这个问题,并提供你的见解。我已经尝试了很多不同的方法,但还没有成功。 :( - TumsAsyncFunction !== Function
的检查存在于避免误报。在转译代码中不会有真实的阳性,因为在转译代码中,async
函数与常规函数没有区别。 - Estus Flaskawait
,它适用于 promises 和非 promises。这就是答案中最后一个片段展示的内容。 - Estus Flaskresult instanceof Promise
) 或可 thenable
对象(result && typeof result.then === 'function'
),而不是检查函数是否为 async
。 - Estus FlasktheFunc.constructor.name == 'AsyncFunction'
async
函数则不行,“theFunc = () => __awaiter(void 0, void 0, void 0, function* () { })”。 - Estus Flask=== 'AsyncFunction'
呢? - themefieldclass AsyncFunction {}
并打破它,但我明白这很不可能。 - Lloyd无论是 @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*
);
}
这是一个非常有价值的问题,我很生气有人对他进行了负面评价。这种类型检查的主要用例是用于库/框架/装饰器。
现在还处于初期阶段,我们不应该对有价值的问题进行负面评价。
fn.constructor.name
,对于async函数将是AsyncFunction
。 - Mike 'Pomax' Kamermansfunction(resolve, reject)
、async function()
或 Promise
。如果没有涉及到转译器,后两者可以很容易地被检测出来。一旦涉及到转译器,就几乎不可能区分 async
和 function(resolve, reject)
之间的差异了。不幸的是,由于代码压缩,提出的解决方案几乎无法工作。很遗憾,转译器没有利用 Promise polyfill,否则会更容易检测。 :/ - tresf如果您正在使用NodeJS 10.x或更高版本
请使用本地实用函数。
util.types.isAsyncFunction(function foo() {}); // Returns false
util.types.isAsyncFunction(async function foo() {}); // Returns true
请牢记以上答案中提到的所有问题。如果一个函数不小心返回了一个 Promise,它将会返回一个错误的负面结果。
此外(来自文档):
请注意,这只报告 JavaScript 引擎所看到的内容;特别是,如果使用了转译工具,则返回值可能与原始源代码不匹配。
但是,如果您在 NodeJS 10 中使用 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()
await
将普通函数转换为 Promise。 - ahmelq以下是David Walsh在他的博客文章中提供的简短实用方法:
const isAsync = myFunction.constructor.name === "AsyncFunction";
干杯!
简短回答:在暴露AsyncFunction
后使用instaceof
。详见下文。
长篇回答:不要这样做。详见下文。
您可以检测函数是否使用async
关键字声明。
当您创建一个函数时,它会显示为Function类型:
> f1 = function () {};
[Function: f1]
instanceof
运算符进行测试:> f1 instanceof Function
true
> f2 = async function () {}
[AsyncFunction: f2]
因此,我们可以使用 instanceof
进行测试:
> f2 instanceof AsyncFunction
ReferenceError: AsyncFunction is not defined
Reference/Global_Objects
下面……如果您需要轻松访问 AsyncFunction
,您可以使用我的 unexposed
模块:
要获取本地变量:
const { AsyncFunction } = require('unexposed');
或者添加一个全局的AsyncFunction
对象,与其他全局对象并列:
require('unexposed').addGlobals();
> f2 = async function () {}
[AsyncFunction: f2]
> f2 instanceof AsyncFunction
true
上面的代码将测试函数是否使用async
关键字创建,但请记住,真正重要的不是函数是如何创建的,而是函数是否返回一个Promise。
无论在哪里使用此“async”函数:
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
以了解其内部是否进行了一些异步/等待操作但未返回任何内容是可以的。 - Amit Kumar Guptathen().catch()
来处理async函数,而是使用try/await
。所以,如果你确实需要知道函数的类型,那么你完全可以检查它的类型,但不要使用instanceof
:使用fn.constructor.name
代替。如果它是AsyncFunction
而不是Function
,那么你就知道它是一个async函数。 - Mike 'Pomax' Kamermansasync function main(callback) {
let result = await callback(); // even if callback is not async
// use 'result'
}
async function main(callback) {
let result = callback();
if (result instanceof Promise) {
result = await result;
}
// use 'result'
}
不要测试你想要高效调用的函数是否是异步函数(即返回一个Promise
),而是可以在不使用await
的情况下直接调用它,检查返回值是否为promise,然后再进行等待:
let result = someFunctionWhichMayBeAsync()
if (typeof result?.then === 'function') {
result = await result
}
这种方法适用于真正的异步函数和任何返回Promise
(或 thenable) 的内容。唯一的注意点是如果返回一个具有不实际存在 thenable 方法的.then
(但在那种情况下,调用await
或Promise.resolve()
也会失败,因此您实际上没有更好的选择)。
可能与无条件await
返回值相比,性能并没有提高多少,因为检查.then
的值可能比await
的开销更大-这里是基准测试。
我经常交替使用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 函数需要调用才能进行验证。
await
都会从根本上改变执行顺序。当await
应用于同步函数调用时,结果是在await
点处,尽管已经完成了调用的同步函数,但包含该调用的异步函数将返回一个未解决的Promise。 - JohnRC
await
等待非 Promise 对象,则会自动将其包装成 Promise。 - Bergiawait
将不必要地引入一个 promise 包装器,并且效率可能会略低。 - JohnRC