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

React中Playwright缓存失效:重复请求URL无法读取缓存求解

React中使用Playwright缓存HTML未命中的问题

我在React应用中运行Playwright,目标是直接返回缓存结果。以下代码可设置和获取缓存,但同一URL再次请求时无法命中缓存,请问我遗漏了什么?

const { chromium } = require('playwright');
const LRU = require('lru-cache');

const cache = new LRU({
  max: 500,  // 缓存最大条目数
  ttl: 86400 // 缓存条目有效期(毫秒)
});

async function fetchHTML(url) {
  const browser = await chromium.launch();
  const context = await browser.newContext();
  const page = await context.newPage();
  page.setDefaultTimeout(10000);
  // 检查URL是否在缓存中
  let html = cache.get(url);
  if (html) {
    // 这里从未执行过 😞
    console.log('从缓存获取HTML');
  } else {
    await page.goto(url);
    // html = await page.content();
    html = await page.evaluate(() => document.body.innerHTML);
    console.log('为' + url + '设置缓存');
    cache.set(url, html);

    let cachedHtml = cache.get(url);
    // 😄 这里却能正常获取 🤷
    console.log('缓存HTML长度: ' + cachedHtml.length); // 返回: 缓存HTML长度: 1234
  }
  await browser.close();
  return html;
}

let url = 'https://example.com';
let html = fetchHTML(url); // 成功获取!
console.log(html.length); // 返回: 1234
// 但缓存生效了吗?
let cachedHtml = fetchHTML(url); // 这次还是重新获取,而非从缓存读取。为什么?
console.log(cachedHtml.length);

问题根源

  1. 异步函数调用未用await:fetchHTML是异步函数,直接调用会返回Promise对象而非实际HTML内容。两次调用几乎同时发起,第一次的缓存写入逻辑还未执行完成,第二次就已经做了缓存检查,自然无法命中。
  2. 错误的日志输出:你看到的html.length实际是打印Promise对象的属性,并非HTML内容的长度,这会误导你以为已经拿到了真实结果。

修复方案

1. 正确使用await调用异步函数

将调用逻辑放在async函数中,用await等待异步操作完成,确保第一次缓存写入完成后再执行第二次请求:

async function run() {
  let url = 'https://example.com';
  // 第一次请求,写入缓存
  let html = await fetchHTML(url);
  console.log('第一次获取的HTML长度:', html.length);
  
  // 第二次请求,命中缓存
  let cachedHtml = await fetchHTML(url);
  console.log('第二次获取的HTML长度:', cachedHtml.length);
}

// 执行主函数
run();

2. 优化浏览器实例复用(可选但关键)

当前代码每次调用fetchHTML都会启动新浏览器,性能极低且浪费资源。可以将浏览器实例初始化移到函数外部,复用上下文:

const { chromium } = require('playwright');
const LRU = require('lru-cache');

const cache = new LRU({
  max: 500,
  ttl: 86400
});

// 提前初始化浏览器,复用实例
let browser;
async function initBrowser() {
  if (!browser) {
    browser = await chromium.launch();
  }
  return browser;
}

async function fetchHTML(url) {
  let html = cache.get(url);
  if (html) {
    console.log('从缓存获取HTML');
    return html;
  }

  const browser = await initBrowser();
  const context = await browser.newContext();
  const page = await context.newPage();
  page.setDefaultTimeout(10000);
  
  await page.goto(url);
  html = await page.evaluate(() => document.body.innerHTML);
  console.log('为' + url + '设置缓存');
  cache.set(url, html);
  
  await context.close(); // 只关闭上下文,保留浏览器实例
  return html;
}

// 主函数
async function run() {
  let url = 'https://example.com';
  let html = await fetchHTML(url);
  console.log('第一次HTML长度:', html.length);
  
  let cachedHtml = await fetchHTML(url);
  console.log('第二次HTML长度:', cachedHtml.length);
  
  // 程序结束时关闭浏览器
  await browser.close();
}

run();

验证效果

修复后第二次调用fetchHTML会触发从缓存获取HTML的日志,说明缓存命中成功。

内容的提问来源于stack exchange,提问作者Ryan

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.06 19:15:52