用户切换浏览器活动标签页时,异步请求或函数是否会暂停执行?
核心结论
你问的场景下,最终记录的API时长是否准确,取决于你对Performance API的具体实现写法,和不同浏览器对后台标签页的调度规则,没有绝对的固定答案,以下是分场景的实测级说明:
各异步特性在后台标签页的实际表现
- 你已经了解的
setTimeout/setInterval、requestAnimationFrame确实会出现后台执行异常:这类依赖渲染线程调度的任务,会被浏览器大幅降频,长时间挂在后台的标签甚至会直接暂停这类任务,等切回前台才补跑,用这类逻辑做计时确实会出现偏差。 - 你用到的其余技术点(fetch、Redux Saga generator、Performance API)本身不存在“后台被强制暂停”的设计问题,但不同模块的行为是分层的:
- 网络栈层面的逻辑(不管是原生fetch、whatwg-fetch封装的XHR请求,还是Performance的网络资源计时条目):完全独立于渲染主线程运行,不管标签页在前台、后台,甚至被浏览器冻结内存,请求的发送、服务器处理、响应回传全流程都不会暂停,响应完成的时间记录是完全真实的墙钟时间。
- JS主线程的代码执行(包括Redux Saga的generator逻辑、promise回调):常规桌面浏览器下,后台标签页只会节流渲染类任务,微任务、promise回调会正常执行;但在移动浏览器、长时间后台被触发内存回收的标签页中,主线程会被完全冻结,所有JS代码都会暂停执行,直到用户切回标签页才恢复运行。
不同实现下的实际计算结果
首先要纠正你代码里的一个常见使用误区:
你贴的现有代码存在Performance API的典型误用:
const { name, get } = api.foo; const { mark, getEntriesByName } = window.performance; mark(name); response = yield call(get, payload); // 切后台、等2分钟切回 const duration = getEntriesByName(name).pop().duration; yield put(actions.logQueryDuration(name, duration))单独调用
mark(name)生成的是纯时间点标记(PerformanceMark类型),这类条目的duration属性恒为0,根本不代表任何时间区间。按你现在的写法,拿到的duration永远是0,完全达不到统计耗时的目的。
基于这个前提,分两种常见的正确实现来算结果:
- 手动计算时间差的写法(请求发起前存
startTime = performance.now(),拿到响应后用performance.now() - startTime算差值)- 无页面冻结的场景(常规桌面端浏览器):请求发出10秒后就拿到完整响应,代码会立刻执行时长计算、dispatch日志动作,记录的结果就是准确的10秒,完全不受用户中途切后台的影响。
- 页面被冻结的场景(移动端浏览器、长时间后台被浏览器挂起回收的标签):请求10秒就已经完成响应,但此时主线程被完全挂起,时长计算的代码根本没有执行机会。等2分钟后用户切回标签页、主线程解冻,才会开始跑后续逻辑——这时候用当前时间点减之前存的开始时间戳,算出来的差值就是2分钟,完全不准,把页面冻结的时长也算进了API耗时里。
- 基于网络栈资源条目取耗时的写法
不管页面有没有被冻结,浏览器都会在网络栈层面自动记录每个HTTP请求的真实耗时,这类条目(PerformanceResourceTiming类型)完全不受主线程状态影响。只要等主线程解冻后,根据请求的URL路径匹配对应的资源计时条目,拿到的duration永远是真实的10秒,不会因为页面后台、冻结出现任何偏差。
针对你当前技术栈的适配建议
- 不要依赖主线程手动计算的时间差统计API耗时,优先通过
performance.getEntriesByType('resource')匹配对应API请求的计时条目拿真实耗时,这种写法在任何后台、冻结场景下都能保证结果准确。 - Redux Saga的
calleffect本身没有任何计时逻辑,只是把异步请求的promise做了一层封装,generator的执行完全跟着promise的状态走,不存在自身的延时或计时偏差问题。 - whatwg-fetch作为兼容垫片,底层基于XHR实现,和原生fetch的网络行为完全一致,同样不会在后台被暂停,不需要额外做特殊适配。
内容的提问来源于stack exchange,提问作者Joshua Barker
相关产品推荐
相关产品推荐

