JavaScript中判断函数是否为异步函数的方法
在JavaScript开发中,我们经常会遇到需要区分同步函数和异步函数的场景,比如在做函数封装、错误捕获或者执行流程控制时,针对异步函数往往需要特殊处理。那么有哪些可靠的方法可以判断一个函数是不是异步函数呢?
什么是异步函数
首先我们需要明确异步函数的定义:在ES2017引入的async/await语法中,被async关键字声明的函数就是异步函数,这类函数执行后会返回一个Promise对象,函数内部的await表达式可以暂停执行,等待异步操作完成后再继续后续逻辑。除了async函数之外,普通的返回Promise的函数也属于异步逻辑,但本文主要讨论如何判断一个函数本身是不是async声明的异步函数。
方法一:通过constructor判断
JavaScript中每个函数都有constructor属性,指向创建该函数的构造函数,而async函数的constructor就是AsyncFunction,这是和其他普通函数最大的区别。我们可以通过判断函数的constructor是否等于AsyncFunction来实现识别。
不过需要注意,AsyncFunction本身并不是全局对象,我们需要先创建一个async函数,再通过它的constructor拿到AsyncFunction的引用,之后就可以用这个引用去判断其他函数了。
// 先获取AsyncFunction的构造函数引用
const AsyncFunction = (async function() {}).constructor;
// 定义测试函数
const asyncFn = async function() {
return await Promise.resolve(1);
};
const syncFn = function() {
return 1;
};
// 判断函数是否为异步函数
function isAsyncFunction(fn) {
return fn && (fn.constructor === AsyncFunction);
}
console.log(isAsyncFunction(asyncFn)); // true
console.log(isAsyncFunction(syncFn)); // false这种方法的逻辑很简单,因为普通函数的constructor是Function,而async函数的constructor是AsyncFunction,两者属于不同的构造函数,所以可以直接通过constructor属性做对比。不过这种方法有一个小局限:如果函数的constructor被人为修改过,判断结果就会出错,但正常开发场景下几乎不会有人去修改函数的constructor,所以实用性还是很高的。
方法二:通过toString()结果判断
每个函数都可以通过toString()方法转成字符串,async函数的toString()结果会以"async function"开头,我们也可以利用这个特征来做判断。
// 定义测试函数
const asyncFn = async function() {
return await Promise.resolve(1);
};
const syncFn = function() {
return 1;
};
// 判断函数是否为异步函数
function isAsyncFunction(fn) {
if (typeof fn !== 'function') {
return false;
}
// 转成字符串后判断是否以async function开头
return fn.toString().startsWith('async function');
}
console.log(isAsyncFunction(asyncFn)); // true
console.log(isAsyncFunction(syncFn)); // false这种方法的实现更直观,不需要去获取AsyncFunction的引用,直接通过函数字符串化的结果做特征匹配。不过它也有局限性:如果有人重写了函数的toString方法,或者函数是通过某些特殊方式生成的,toString的结果可能不符合预期,导致判断错误。
方法三:通过instanceof判断
和方法一类似,我们也可以直接用instanceof关键字判断函数是否是AsyncFunction的实例,本质和方法一的逻辑是一致的。
// 先获取AsyncFunction的构造函数引用
const AsyncFunction = (async function() {}).constructor;
// 定义测试函数
const asyncFn = async function() {
return await Promise.resolve(1);
};
const syncFn = function() {
return 1;
};
// 判断函数是否为异步函数
function isAsyncFunction(fn) {
return fn instanceof AsyncFunction;
}
console.log(isAsyncFunction(asyncFn)); // true
console.log(isAsyncFunction(syncFn)); // falseinstanceof的判断逻辑是检测函数的原型链上是否有AsyncFunction的原型,因为async函数的原型链上会有AsyncFunction.prototype,而普通函数没有,所以也能准确判断。这种写法和constructor判断相比,在原型链被修改的场景下也可能出错,但同样在正常开发场景中很少遇到这类问题。
不同方法的对比与选择
下面我们把三种方法的优缺点整理成表格,方便大家根据实际场景选择:
| 方法 | 优点 | 缺点 |
|---|---|---|
| constructor判断 | 逻辑简单,执行效率高 | 依赖AsyncFunction构造函数引用,函数constructor被修改时会失效 |
| toString()判断 | 不需要额外获取构造函数引用,实现简单 | 函数toString被重写时失效,箭头函数如果是async的也能正确识别,但普通箭头函数不会影响 |
| instanceof判断 | 语义清晰,符合JavaScript原型判断的习惯 | 和方法一一样,依赖AsyncFunction引用,原型链被修改时失效 |
如果是在常规的业务开发场景中,三种方法都可以使用,其中constructor和instanceof的判断准确率更高,因为toString的结果更容易被意外修改。如果你的代码需要在特殊环境下运行,比如某些对函数原型做了限制的环境,也可以优先选择toString的方式。
注意事项
需要特别说明的是,以上方法判断的都是函数本身是不是用async声明的异步函数,而不是判断函数是不是执行了异步逻辑。比如下面这个函数虽然返回Promise,属于异步逻辑,但它本身不是async函数,所以以上方法的判断结果都会是false:
// 返回Promise的普通函数,不是async函数
const promiseFn = function() {
return Promise.resolve(1);
};
const AsyncFunction = (async function() {}).constructor;
console.log(promiseFn.constructor === AsyncFunction); // false
console.log(promiseFn.toString().startsWith('async function')); // false
console.log(promiseFn instanceof AsyncFunction); // false如果我们需要判断一个函数执行后是否会返回Promise,那需要调用函数后看返回结果是否是Promise实例,这和判断函数本身是否为async函数是两个不同的需求,使用的时候不要混淆。
JavaScript异步函数async函数判断方法AsyncFunction 本作品最后修改时间:2026-05-23 23:07:22