封装Puppeteer的page on response到函数后出现超时问题求助
问题描述
我试图将页面响应拦截逻辑封装到名为setupRequestInterception的独立函数中,但封装后的代码会触发超时无法正常运行;而将逻辑直接写在主执行代码里时却能正常工作。以下是两段对比代码:
封装后超时的代码
import puppeteer from 'puppeteer'; const setupRequestInterception = async (page) => { await page.setRequestInterception(true); return new Promise((resolve, reject) => { page.on('request', (request) => { request.continue(); }); page.on('response', async (response) => { const headers = response.headers(); resolve(headers); // Resolve the promise with the headers }); page.on('close', () => { reject(new Error('Page closed before headers could be intercepted.')); }); setTimeout(() => { reject(new Error('Timeout reached before headers could be intercepted.')); }, 10000); // Set a timeout to prevent hanging }); }; (async () => { const browser = await puppeteer.launch(); const page = await browser.newPage(); try { const headers = await setupRequestInterception(page); await page.goto('https://google.com'); console.log('Intercepted Headers:', headers); } catch (err) { console.log('Error:', err.message); } finally { await browser.close(); } })();
未封装可正常运行的代码
(async () => { const browser = await puppeteer.launch(); const page = await browser.newPage(); try { const headers = new Promise((resolve, reject) => { page.on('response', async (response) => { const headers = response.headers(); resolve(headers); // Resolve the promise with the headers }); page.on('close', () => { reject(new Error('Page closed before headers could be intercepted.')); }); setTimeout(() => { reject(new Error('Timeout reached before headers could be intercepted.')); }, 10000); // Set a timeout to prevent hanging }); await page.goto('https://google.com'); console.log('Intercepted Headers:', headers); } catch (err) { console.log('Error:', err.message); } finally { await browser.close(); } })();
问题原因
封装代码的核心问题是执行顺序逻辑错误:主代码里先await setupRequestInterception(page),此时函数内部的Promise会一直等待response事件触发,但这时候还没调用page.goto发起请求,根本不会产生任何响应,自然会触发超时。
而未封装的代码是先创建监听response的Promise,再调用page.goto发起请求,当响应返回时,Promise就能正常resolve。
修复方案
调整执行顺序,先调用setupRequestInterception获取监听Promise,再发起页面跳转,最后等待Promise resolve。同时可以优化拦截逻辑——如果只是需要获取响应头,完全不需要开启requestInterception(该API主要用于修改/拦截请求,开启后必须手动处理每个请求,反而会增加不必要的性能开销)。
修复后的代码:
import puppeteer from 'puppeteer'; const setupRequestInterception = (page) => { return new Promise((resolve, reject) => { // 定义响应监听函数,触发后移除监听避免重复执行 const onResponse = async (response) => { const headers = response.headers(); page.off('response', onResponse); resolve(headers); }; page.on('response', onResponse); // 页面关闭时的处理,同样移除监听 const onClose = () => { page.off('close', onClose); reject(new Error('Page closed before headers could be intercepted.')); }; page.on('close', onClose); // 超时处理,同时清理监听避免内存泄漏 const timeoutId = setTimeout(() => { page.off('response', onResponse); page.off('close', onClose); reject(new Error('Timeout reached before headers could be intercepted.')); }, 10000); }); }; (async () => { const browser = await puppeteer.launch(); const page = await browser.newPage(); try { // 先获取监听Promise,不要提前await const headersPromise = setupRequestInterception(page); // 发起页面请求 await page.goto('https://google.com'); // 等待响应头拦截结果 const headers = await headersPromise; console.log('Intercepted Headers:', headers); } catch (err) { console.log('Error:', err.message); } finally { await browser.close(); } })();
额外说明
- 若仅需获取响应头,无需开启
requestInterception,减少不必要的性能消耗。 - 添加
page.off()移除监听,既避免后续响应重复触发resolve/reject,也防止内存泄漏。
内容的提问来源于stack exchange,提问作者MegaKaskaskas
相关产品推荐
相关产品推荐

