如何无需逐函数加try/catch 统一替换Puppeteer异步错误为自定义错误
统一处理Puppeteer非结构化错误的实现方案
逐方法编写try/catch的冗余度太高,可通过通用包装逻辑全局统一转换错误格式,不需要为每个异步方法单独写异常捕获。
方案1:高阶函数批量包装异步方法
先定义可复用的错误包装逻辑,所有Puppeteer相关异步方法统一走该逻辑处理:
// 自定义结构化错误类 class PuppeteerOperationError extends Error { constructor(originalError, extendInfo = {}) { super(originalError.message); this.name = 'PuppeteerOperationError'; // 自动从原始错误文本中提取可执行文件路径 this.executablePath = extendInfo.executablePath || this._parseExecPath(originalError.message); this.originalStack = originalError.stack; } _parseExecPath(errorText) { const pathMatch = errorText.match(/spawn\s+([^\s]+)\s+ENOENT/); return pathMatch ? pathMatch[1] : null; } } // 通用异步错误包装器 function wrapAsyncError(asyncFunc, extendInfo = {}) { return async (...args) => { try { return await asyncFunc(...args); } catch (err) { throw new PuppeteerOperationError(err, extendInfo); } } }
改造原有业务类时,只需要在初始化阶段批量包装需要用到的Puppeteer方法即可,不需要逐个写try/catch:
class Application { constructor() { this.browser = null; // 预包装puppeteer根模块方法 this.launch = wrapAsyncError(puppeteer.launch.bind(puppeteer)); } async init(options) { this.browser = await this.launch(options); // 实例化browser后,批量包装browser层的方法 ['close', 'newPage', 'pages'].forEach(methodName => { const originalMethod = this.browser[methodName].bind(this.browser); this.browser[methodName] = wrapAsyncError(originalMethod); }); } // 业务方法直接调用即可,异常会自动转为结构化错误 async close() { await this.browser.close(); } }
方案2:Proxy全局自动代理(零手动枚举方法)
如果不想手动枚举需要包装的方法名,可以用Proxy做一层代理,访问到的所有异步方法会自动套上错误包装逻辑,包括后续生成的page实例上的方法也会被自动处理:
function createAutoWrapProxy(target, extendInfo = {}) { return new Proxy(target, { get(target, prop, receiver) { const value = Reflect.get(target, prop, receiver); // 函数类型自动包装错误捕获 if (typeof value === 'function') { return wrapAsyncError(value.bind(target), extendInfo); } // 嵌套对象递归代理,覆盖page、elementHandle等子实例 if (typeof value === 'object' && value !== null) { return createAutoWrapProxy(value, extendInfo); } return value; } }) }
使用时只需要在初始化阶段代理一次Puppeteer模块,后续所有调用都自动生效:
class Application { constructor() { // 全局代理puppeteer模块,一次配置所有方法自动包装 this.puppeteer = createAutoWrapProxy(puppeteer); this.browser = null; } async launch(options) { this.browser = await this.puppeteer.launch(options); return this.browser; } async runTest() { const page = await this.browser.newPage(); // page实例下的goto、click、waitForSelector等所有异步方法都已自动包装 await page.goto('https://test.local'); } async close() { await this.browser.close(); } }
实现说明
- 结构化错误类内置了路径解析逻辑,会自动从Puppeteer抛出的原始错误文本中提取找不到的浏览器可执行文件路径,不需要手动传入参数
- 所有包装逻辑会保留原始错误栈信息,方便排查问题,同时可以根据业务需要扩展更多结构化字段,比如错误码、操作类型等
- 两种方案都完全消除了逐方法编写try/catch的冗余代码,所有Puppeteer抛出的异常会被统一转换为自定义结构化错误
内容的提问来源于stack exchange,提问作者imagesck
相关产品推荐
相关产品推荐

