Reflect.apply 不能直接监控异步函数执行过程,需包裹 async 函数并基于其返回的 Promise 在 then/catch 中打点,记录起止时间、计算耗时、上报状态与错误。
reflect.apply 本身不能直接监控异步函数的执行过程,因为它只是同步调用函数并返回结果(对 async 函数,返回的是 promise,而非 await 后的值)。要实现对异步函数的性能打点监控,关键不是替换调用动作,而是包裹异步函数、在 promise 生命周期的关键节点(如开始、resolve、reject)插入时间戳和上报逻辑。
核心思路:用 Reflect.apply 包裹 async 函数,但监控逻辑必须作用于 Promise
你可以用 Reflect.apply 触发原函数调用,拿到返回的 Promise,再对这个 Promise 进行链式处理(.then/.catch),从而捕获执行耗时与状态。这种方式保持了调用签名一致,又不侵入原始函数定义。
- 调用前记录 startTime
- 用 Reflect.apply 执行原函数,获取 Promise 实例
- 在 Promise.then 和 Promise.catch 中记录 endTime,并计算 duration
- 统一上报指标(如函数名、参数摘要、耗时、是否成功、错误信息等)
基础监控包装器示例
下面是一个可复用的监控高阶函数:
function monitorAsync(fn, name = fn.name || 'anonymous') {
return function(...args) {
const startTime = performance.now();
const promise = Reflect.apply(fn, this, args);
<pre class='brush:php;toolbar:false;'>return promise
.then(result => {
const duration = performance.now() - startTime;
console.log(`✅ ${name} success: ${duration.toFixed(2)}ms`);
// 上报逻辑:sendToMonitor({ name, duration, status: 'success', ... })
return result;
})
.catch(error => {
const duration = performance.now() - startTime;
console.error(`❌ ${name} failed: ${duration.toFixed(2)}ms`, error);
// 上报逻辑:sendToMonitor({ name, duration, status: 'error', error: String(error) })
throw error;
});}; }
使用方式:
async function fetchUser(id) {
await new Promise(r => setTimeout(r, 300));
return { id, name: 'Alice' };
}
<p>const monitoredFetch = monitorAsync(fetchUser, 'api.fetchUser');
monitoredFetch(123).then(console.log);
// ✅ api.fetchUser success: 302.45ms
// { id: 123, name: 'Alice' }</p>进阶:支持上下文透传与错误分类
真实场景中,你可能需要保留 this 上下文、区分网络错误/业务错误、避免监控逻辑影响原始 reject 堆栈。可增强如下:
- 用 Reflect.apply(fn, this, args) 确保 this 和参数完全透传
- 在 catch 中用 error instanceof TypeError 或检查 error.name 判断错误类型
- 使用 Promise.resolve(promise).then(...).catch(...) 统一处理,兼容非 Promise 返回值(防御性写法)
- 添加采样率控制(如只监控 10% 的调用),避免日志爆炸
注意事项与边界情况
需警惕几个易错点:
- 不要 await Reflect.apply:它返回的是 Promise,await 它会阻塞,失去“监控不干预流程”的初衷
- 避免重复包装:对已监控的函数再次 monitorAsync,会导致嵌套计时失真
- performance.now() 在 Node.js 需用 process.hrtime.bigint() 或 perf_hooks,浏览器环境才可用 performance
- 未被捕获的 rejection(unhandledrejection)需单独监听,上述包装仅覆盖显式 catch 场景
不复杂但容易忽略:真正的性能瓶颈常在 await 后的处理逻辑或后续链式操作,单靠入口打点不够。建议结合 Promise.then 链中关键步骤插桩,或使用 async_hooks(Node.js)做全链路追踪。


















