Playwright脚本监听自定义performance.mark()条目时出现永久阻塞的问题求助
Playwright脚本监听自定义performance.mark()条目时出现永久阻塞的问题求助
嗨,我来帮你梳理下这个问题,你的思路其实是对的,但监听mark类型时卡住大概率是这几个原因导致的,给你一步步分析和解决方案:
为什么你的脚本会卡住?
你的现有代码里,PerformanceObserver监听的是所有mark类型的条目,但没有过滤你需要的特定标记(也就是你设置的"example-function-loaded"),而且存在两个关键问题:
- 如果页面在执行这段代码时还没有任何
mark条目,PerformanceObserver的回调根本不会触发,导致Promise一直处于pending状态,page.evaluate就会永远等下去。 - 就算页面之前有其他无关的
mark,你的代码会直接取最后一个mark就resolve,但这大概率不是你要等的那个后台函数完成的标记,反而会导致后续测试提前执行,结果不符合预期。
另外,你没有给Promise设置超时兜底,一旦目标mark没被正确添加(比如后台函数报错、执行时机超预期),脚本就会无限阻塞。
修复后的监听脚本(针对自定义mark)
我给你调整了代码,专门过滤你需要的标记,还加了超时和预检查,避免卡住:
// 假设你的自定义标记名称是 "example-function-loaded" try { const customMarkStartTime = await page.evaluate(() => { return new Promise((resolve, reject) => { // 先预检查:如果目标mark已经存在(比如后台函数在Observer创建前就执行完了),直接返回 const existingTargetMarks = performance.getEntriesByName("example-function-loaded"); if (existingTargetMarks.length > 0) { resolve(existingTargetMarks.at(-1).startTime); return; } // 只监听我们需要的特定mark const observer = new PerformanceObserver((entryList) => { const targetEntries = entryList.getEntriesByName("example-function-loaded"); if (targetEntries.length > 0) { const targetEntry = targetEntries.at(-1); resolve(targetEntry.startTime); observer.disconnect(); // 拿到结果后断开监听,避免内存泄漏 } }); observer.observe({ type: "mark", buffered: true }); // 超时兜底:30秒没等到就抛出错误,避免永久阻塞 setTimeout(() => { observer.disconnect(); reject(new Error("超时:未等到目标performance标记")); }, 30000); }); }); console.log(`后台函数完成时间(基于mark):${customMarkStartTime}`); } catch (err) { console.error("监听自定义mark失败:", err); }
其他更可靠的替代方案
如果修改页面代码对你来说可行,我更推荐用自定义事件的方式,比依赖PerformanceObserver更直接:
- 先在你的页面代码里,后台函数完成后触发一个自定义事件:
// 后台任务完成时执行 function onBackgroundTaskComplete() { performance.mark("example-function-loaded"); // 触发自定义事件 window.dispatchEvent(new CustomEvent("background-task-done")); }
- 然后在Playwright脚本里监听这个事件:
// 等待自定义事件触发,或者检查mark是否已存在 await page.waitForFunction(() => { return new Promise((resolve) => { // 先检查mark是否已经存在 if (performance.getEntriesByName("example-function-loaded").length > 0) { resolve(true); return; } // 监听自定义事件,触发后结束等待 window.addEventListener("background-task-done", () => resolve(true), { once: true }); }); }, { timeout: 30000 }); // 这里也可以设置超时 // 之后获取mark的时间 const markTime = await page.evaluate(() => { const targetMark = performance.getEntriesByName("example-function-loaded").at(-1); return targetMark?.startTime; });
调试小技巧
如果还是有问题,可以先做这些调试:
- 在浏览器DevTools的Console里执行
performance.getEntriesByType('mark'),确认你的自定义mark确实被添加了。 - 在Playwright卡住的代码前,先打印当前页面的所有mark:
console.log("当前页面已有的mark:", await page.evaluate(() => performance.getEntriesByType('mark')));
- 给你的
page.evaluate单独设置超时,比如page.evaluate(..., { timeout: 15000 }),这样能快速判断是不是因为没等到mark而阻塞。
希望这些建议能帮到你,有其他问题随时补充~
内容来源于stack exchange
相关产品推荐
相关产品推荐

