You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Playwright脚本监听自定义performance.mark()条目时出现永久阻塞的问题求助

Playwright脚本监听自定义performance.mark()条目时出现永久阻塞的问题求助

嗨,我来帮你梳理下这个问题,你的思路其实是对的,但监听mark类型时卡住大概率是这几个原因导致的,给你一步步分析和解决方案:

为什么你的脚本会卡住?

你的现有代码里,PerformanceObserver监听的是所有mark类型的条目,但没有过滤你需要的特定标记(也就是你设置的"example-function-loaded"),而且存在两个关键问题:

  1. 如果页面在执行这段代码时还没有任何mark条目,PerformanceObserver的回调根本不会触发,导致Promise一直处于pending状态,page.evaluate就会永远等下去。
  2. 就算页面之前有其他无关的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更直接:

  1. 先在你的页面代码里,后台函数完成后触发一个自定义事件:
// 后台任务完成时执行
function onBackgroundTaskComplete() {
  performance.mark("example-function-loaded");
  // 触发自定义事件
  window.dispatchEvent(new CustomEvent("background-task-done"));
}
  1. 然后在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;
});

调试小技巧

如果还是有问题,可以先做这些调试:

  1. 在浏览器DevTools的Console里执行performance.getEntriesByType('mark'),确认你的自定义mark确实被添加了。
  2. 在Playwright卡住的代码前,先打印当前页面的所有mark:
console.log("当前页面已有的mark:", await page.evaluate(() => performance.getEntriesByType('mark')));
  1. 给你的page.evaluate单独设置超时,比如page.evaluate(..., { timeout: 15000 }),这样能快速判断是不是因为没等到mark而阻塞。

希望这些建议能帮到你,有其他问题随时补充~

内容来源于stack exchange

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.04.08 09:15:32