Puppeteer中setTimeout延迟失效,浏览器提前关闭报错求助
Puppeteer延迟后浏览器提前关闭问题排查
问题描述
我创建了waitForTimeout函数,想在Puppeteer打开新页面后实现可await的延迟,但实际浏览器打开页面后立即关闭,只有Node端会延迟10秒,随后抛出页面已关闭的错误。按逻辑await browser.close()应该在延迟完成后执行,却出现异常。
原代码
function waitForTimeout(timeout) { return new Promise((resolve) => { setTimeout(resolve, timeout) }) } const puppet = () => { getCookies(async (cookies) => { const browser = await puppeteer.launch({ headless: false }); const page = await browser.newPage(); await page.setCookie(...cookies); await page.goto("https://ipchicken.com"); await waitForTimeout(10000) let data = await page.evaluate(()=>{ const ip = document.querySelector("body > table:nth-child(2) > tbody > tr > td:nth-child(3) > p:nth-child(2) > font > b").textContent return ip; }) console.log(data) await browser.close(); }); }
报错信息
/Users/macbook/repos/operation-tss/node_modules/puppeteer-core/lib/cjs/puppeteer/common/Connection.js:316 return Promise.reject(new Error(`Protocol error (${method}): Session closed. Most likely the ${__classPrivateFieldGet(this, _CDPSessionImpl_targetType, "f")} has been closed.`)); ^ Error: Protocol error (Runtime.callFunctionOn): Session closed. Most likely the page has been closed. at CDPSessionImpl.send (/Users/macbook/repos/operation-tss/node_modules/puppeteer-core/lib/cjs/puppeteer/common/Connection.js:316:35) at ExecutionContext._ExecutionContext_evaluate (/Users/macbook/repos/operation-tss/node_modules/puppeteer-core/lib/cjs/puppeteer/common/ExecutionContext.js:211:46) at process.processTicksAndRejections (node:internal/process/task_queues:95:5) at async ExecutionContext.evaluate (/Users/macbook/repos/operation-tss/node_modules/puppeteer-core/lib/cjs/puppeteer/common/ExecutionContext.js:107:16)
问题原因
核心问题是回调嵌套导致的异步控制流混乱:
- 你将
async函数作为回调传给了getCookies,但getCookies是传统的回调式API,不会处理async函数返回的Promise。这意味着回调内部的异步操作(比如await puppeteer.launch、await waitForTimeout)会脱离外部控制流的管理。 - 当
getCookies调用完回调后,它的任务就已完成,puppet函数也会立即执行完毕。此时回调内的异步操作处于“游离”状态,可能因为Node.js事件循环的资源回收逻辑,或者getCookies内部的上下文销毁逻辑,导致Puppeteer的页面实例被提前关闭,最终在执行page.evaluate时报错。
解决方法
将基于回调的getCookies转换为Promise形式,使用线性的async/await控制流替代回调嵌套,确保所有异步操作都被正确等待。
修改后的代码
const util = require('util'); function waitForTimeout(timeout) { return new Promise((resolve) => { setTimeout(resolve, timeout) }) } const puppet = async () => { // 将回调式的getCookies转换为Promise形式 const getCookiesPromise = util.promisify(getCookies); const cookies = await getCookiesPromise(); const browser = await puppeteer.launch({ headless: false }); const page = await browser.newPage(); await page.setCookie(...cookies); await page.goto("https://ipchicken.com"); // 也可以直接使用Puppeteer内置的延迟方法,替代自定义的waitForTimeout // await page.waitForTimeout(10000); await waitForTimeout(10000); const data = await page.evaluate(() => { const ip = document.querySelector("body > table:nth-child(2) > tbody > tr > td:nth-child(3) > p:nth-child(2) > font > b").textContent; return ip; }); console.log(data); await browser.close(); };
额外优化
可以直接使用Puppeteer内置的page.waitForTimeout()方法,无需自定义waitForTimeout函数,更符合Puppeteer的官方使用规范。
内容的提问来源于stack exchange,提问作者Matt George
相关产品推荐
相关产品推荐

