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

封装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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.18 22:59:54