无法导出从 puppeteer 浏览器创建的 wsEndpoint

问题描述

我试图在启动时打开 puppeteer 浏览器,然后导出 wsEndpoint,以便我可以使用该链接连接到浏览器,而不是每次调用函数时都打开一个新浏览器。

这是文件 app.js 中的代码片段,它是节点的入口点。

const mainFunction = async () => {
    const browser = await puppeteer.launch()
    const wsEndpoint = browser.wsEndpoint()
    return wsEndpoint
}

mainFunction().then(async endpoint => {
    console.log(endpoint)
    module.exports = endpoint
})

启动时,上面的控制台日志返回一个链接,然后我将其导出 这是实用程序文件 equities.js

中的代码片段
const puppeteer = require("puppeteer")
const endpoint = require("../../app.js")
module.exports = async(symbol)=>{
  console.log(endpoint)
  const browser = await puppeteer.connect({connectWSEndpoint: endpoint})

}

每次调用函数时,控制台日志只返回一个空对象,这意味着 app.js 中的导出由于某种原因失败。我试图用谷歌搜索一些东西并尝试不同的导出方式,但似乎都不起作用。有人可以帮助指导我吗?在此先感谢您。

解决方法

我觉得这里有一些地方不对劲——这段代码感觉好像没有在整个过程中进行过测试,导致了多个故障点。尝试采取较小的步骤,以便您可以隔离问题而不是累积问题。


对于初学者来说,mainFunction 代码放弃了 browser 对象,创建泄漏的子进程资源无法关闭。

我会将 browser 变量与端点一起返回或存储,以便有人可以通过函数清理它。或者只是返回 browser 并让客户端代码根据需要将端点拉出它,以及管理和关闭资源。


接下来是导出代码:

mainFunction().then(async endpoint => {
    console.log(endpoint)
    module.exports = endpoint
})

我不明白这个额外的 then 包装器接收一个从不使用 asyncawait 解析函数的动机。您可能认为 Node await 包含所有这些代码,然后在客户端文件的 module.exports 同步运行之前设置 require 值。那是 not the case,可以用一段更简单的代码来确定:

app.js(为了方便起见,在这篇文章的同一个文件夹中):

const mainFunction = async () => 42;

mainFunction().then(async endpoint => {
    console.log("endpoint":,endpoint)
    module.exports = endpoint
})

index.js:

const endpoint = require("./app");

console.log("imported:",endpoint);

node index 给我:

imported: {}
endpoint: 42

promise 在 require 之后解析,它同步引入了默认的空白对象 module.exports —— 可能不是你所期望的。

如果你有异步代码,它必须永远保持异步,包括导出和导入。尝试直接导出 Promise,然后在客户端中await

app.js:

const mainFunction = async () => 42;
module.exports = mainFunction;

index.js:

const getEndpoint = require("./app");

getEndpoint().then(endpoint => console.log("imported:",endpoint));

运行 node index 给我:imported: 42


equities.js 中的客户端代码看起来更合理,因为它同步导出了一个 promise,但它必须await 在任何使用它的地方导入它的 endpoint promise。

此外,Puppeteer 抛出 puppeteer.connect({connectWSEndpoint: endpoint})Error: Exactly one of browserWSEndpoint,browserURL or transport must be passed to puppeteer.connect。我会让你根据你的目标来解决这个问题。

这是修复承诺问题的重写草图,但这只是一个概念证明,需要进行调整才能完成您想做的任何事情:

app.js:

const puppeteer = require("puppeteer");

const browserPromise = puppeteer.launch();

const endpointPromise = browserPromise
  .then(browser => browser.wsEndpoint())
;

module.exports = {browserPromise,endpointPromise};

equities.js:

const puppeteer = require("puppeteer");
const {browserPromise,endpointPromise} = require("./app");

module.exports = async symbol => {
  const endpoint = await endpointPromise;
  console.log(endpoint);
  //const browser = await puppeteer.connect({connectWSEndpoint: endpoint}) // FIXME
  const browser = await browserPromise;
  await browser.close();
};

index.js:

const equitiesFn = require("./equities");

(async () => {
  await equitiesFn();
})();

运行 node index,您应该会看到 ws 打印出来。

请注意,如果需要,您可以将导出的 Promise 包装在函数中或作为对象的一部分,这是一个更典型的库接口抽象层。但这并没有改变基本的异步性。客户端将调用导出的函数并通过额外的间接层等待端点和/或浏览器承诺,

require("./app").getBrowser().then(browser => /* */);

对比

require("./app").browserPromise.then(browser => /* */);

如果您不想公开浏览器对象,那很好,但我建议公开一个关闭底层浏览器的函数,以便您可以干净地退出,例如

app.js:

const puppeteer = require("puppeteer");

const browserPromise = puppeteer.launch();

const closeBrowser = () => 
  browserPromise.then(browser => browser.close())
;

module.exports = {closeBrowser};

index.js:

require("./app")
  .closeBrowser()
  .then(() => console.log("closed"))
;