判断函数是否为异步函数应仅依赖静态特征,如 object.prototype.tostring.call(fn).includes('asyncfunction');混入执行或 await 会引发误判或异常,proxy 和不当 mock 同样干扰判断。

判断一个函数是否为异步函数,本身是同步行为;但若在判断过程中触发或混入异步操作(如调用、await、副作用读取),就会导致类型误判或执行异常。核心问题不在于“如何识别”,而在于“如何避免识别过程被异步逻辑污染”。
别在判断逻辑里执行函数
常见错误是把 typeof fn === 'function' 和 fn() 或 await fn() 混在一起。一旦函数内部含 await、定时器、fetch 等,执行它就不再是纯类型检查,而是启动了异步流程。
- ✅ 正确做法:仅通过函数对象自身属性判断,例如检查
fn.constructor.name === 'AsyncFunction'或Object.prototype.toString.call(fn) === '[object AsyncFunction]' - ❌ 错误做法:写成
if (await isAsync(fn)) { ... }—— 这会让判断函数自己变成异步,失去“类型判断”的本意
警惕装饰器和 Proxy 的隐式异步调用
有些工具函数或框架(如某些状态库的自动包装)会用 Proxy 拦截函数调用,并在 get 或 apply 中插入异步逻辑。此时即使你只是访问 fn.name 或 fn.length,也可能意外触发 await。
- 检查函数是否被 Proxy 包裹:可用
!fn.hasOwnProperty('then') && !fn[Symbol.toStringTag]辅助排除 Promise-like 伪装 - 对可疑函数,先用
Object.getPrototypeOf(fn)查看原始构造器,再比对AsyncFunction
统一用静态特征做判断,不依赖运行时行为
异步函数的本质特征是语言层面的构造器标识,而非“会不会 await”。只要它由 async function、async () => 或 async class 方法定义,其构造器就是 AsyncFunction。
- 推荐判断方式:Object.prototype.toString.call(fn).includes('AsyncFunction')
- 兼容旧环境可降级为:
fn instanceof Function && fn.constructor && fn.constructor.name === 'AsyncFunction' - 避免使用
fn.then存在性判断——普通函数也可手动添加 then 属性,造成误报
测试时注意 mock 的同步性
单元测试中若用 Jest 或 Vitest mock 异步函数,要确保 mock 实现本身是同步返回 Promise,而不是在 mock 内部又套一层 setTimeout 或真实 fetch。否则类型判断逻辑可能在测试中因竞态而偶然失败。
- ✅ 好的 mock:
jest.fn().mockResolvedValue(42) - ❌ 危险的 mock:
jest.fn(() => new Promise(r => setTimeout(() => r(42), 10)))—— 引入了不确定延迟,干扰判断时机











