Puppeteer: 关闭最后一页时关闭浏览器有时会引发错误

创建于 2018-03-27  ·  9评论  ·  资料来源: puppeteer/puppeteer

告诉我们您的环境:

  • 木偶版:1.2.0
  • 平台/操作系统版本:CentOS 7.4.1708
  • 网址(如果有):不适用
  • Node.js版本:v8.10.0

哪些步骤将重现该问题?

const puppeteer = require('puppeteer');

const run = async () => {
  const browser = await puppeteer.launch();

  // close any open pages
  let pages = await browser.pages();
  await Promise.all(pages.map(async page => await page.close()));

  // handle a page being closed
  browser.on('targetdestroyed', async target => {
    const openPages = await browser.pages();
    console.log('Open pages:', openPages.length);
    if (openPages.length == 0) {
      console.log('Closing empty browser');
      await browser.close();
      console.log('Browser closed');
    }
  });

  pages = await browser.pages();
  for (let i = 0; i < 5; i++) {
    await browser.newPage();
  }

  pages = await browser.pages();
  pages.forEach((page) => page.close());
};

run();

预期的结果是什么?

Count: 0
Open pages: 4
Open pages: 3
Open pages: 2
Open pages: 1
Open pages: 0
Closing empty browser
Browser closed

会发生什么呢?

Open pages: 4
Open pages: 3
Open pages: 2
Open pages: 1
Open pages: 0
Closing empty browser
(node:29074) UnhandledPromiseRejectionWarning: Error: Protocol error (Target.closeTarget): Target closed.
    at Promise (node_modules/puppeteer/lib/Connection.js:86:56)
    at new Promise (<anonymous>)
    at Connection.send (node_modules/puppeteer/lib/Connection.js:85:12)
    at Page.close (node_modules/puppeteer/lib/Page.js:802:36)
    at pages.forEach (test.js:27:32)
    at Array.forEach (<anonymous>)
    at run (test.js:27:9)
    at <anonymous>
    at process._tickCallback (internal/process/next_tick.js:188:7)
(node:29074) UnhandledPromiseRejectionWarning: Unhandled promise rejection. This error originated either by throwing inside of an async function without a catch block, or by rejecting a promise which was not handled with .catch(). (rejection id: 1)
(node:29074) [DEP0018] DeprecationWarning: Unhandled promise rejections are deprecated. In the future, promise rejections that are not handled will terminate the Node.js process with a non-zero exit code.
Browser closed

这不会在每次运行时都发生,但会时常发生。 我完全有可能在这里做某事_bad_!

bug chromium

所有9条评论

+1

进行了一些快速调试,在确认page.close()消息之前(或者说,木偶已处理了确认),将触发targetDestroyed事件。

我可以想办法解决它-例如,在处理targetDestroyed事件之前,先等待Target.closeTarget确认。 如何正确做到这一点是另一个问题!

感谢@MJPA的调查。

问题来自以下事实: browser在关闭关联页面之前从列表中删除了目标。

在下面的示例中,我希望browser.pages()报告1页,直到page.close()调用完成:

const puppeteer = require('puppeteer');

(async () => {
  const browser = await puppeteer.launch();
  const [page] = await browser.pages();
  browser.on('targetdestroyed', async () => console.log('Target destroyed. Pages count: ' + (await browser.pages()).length));
  console.log('closing page...');
  await page.close();
  console.log('page closed.');
  await browser.close();
})();

输出:

closing page...
Target destroyed. Pages count: 0
page closed.

@JoelEinbinder你怎么看?

@MJPA相同的问题

        await page.waitForSelector('aside.aside a[href="/checkout"]')
    .then(() => {
        logMessage('Checkout is available');
    })
    .catch(err => {
        log('Checkout not available');
        success = false;
        browser.close();
    });
    // click checkout button
    await Promise.all([
        page.click('aside.aside a[href="/checkout"]'),
        page.waitForNavigation()
    ]);

在这种情况下,总是发生相同的错误。

也许是这个操纵up的错误。

我在实现上稍有相似,但通过听page上的close事件,得到了相同的错误。 该文档并未对“关闭”的含义过分明确,但是根据其实现,已关闭close _before_页面已关闭,因此以下示例不起作用,类似于targetdestroyed示例。

const puppeteer = require('puppeteer');
const browser = await puppeteer.launch();

// create 5 test pages
for(let i=0; i<5; i++){

  let page = await browser.newPage();

  // listen to page close event and close browser
  // when all get closed
  page.on('close', async () => {
    let pages = await browser.pages();
    if(!pages.length){
      // will not get here!
      browser.close();
    }
  });

}

let pages = await browser.pages();

pages.forEach(async (page) => await page.close());  

我认为这种_sort of_是有意义的,因为该事件称为close而不是closed 。 我通过在close() promise回调中发出自己的closed事件并听取了它的信息来解决这个问题,_does_起作用。

page.close().then(() => {
  page.emit('closed');
});

这个故事的寓意似乎是没有100%保证的方法来知道何时安全关闭浏览器(而不必捕获异常)。 如果我缺少什么,请告诉我。

可能与#3423有关?

关闭浏览器中的所有标签,然后尝试打开新页面后,出现此错误

在以下示例中,我希望browser.pages()报告1页,直到page.close()调用完成:

这将破坏上面示例中的代码。 最后关闭的页面brower.pages()。length为1,浏览器不会关闭。 这样就不会再有目标破坏事件发生,浏览器将永远存在。

我会说这是用户错误,尽管它很微妙。 在调用browser.close()之前,应等待对page.close任何调用。

如果没有用户控制的页面,只需Promise。所有页面都将关闭,然后关闭浏览器。 应该没有必要听targetdestroyed事件。 或者只是调用browser.close并让所有页面自行关闭。

如果有用户控制的页面,则听targetdestroyed事件,但不必调用page.close

+1。
使用page.on("popup")关闭弹出窗口后,我开始看到此错误。 当弹出式窗口在Frame上下文中触发时,有时我无法使用“ popup.close()”将其关闭,并且操纵up的人使选项卡保持打开状态,因此当我尝试关闭整个浏览器时,它会引发“ Target Closed”错误,然后脚本停止。

我使用带有Chrome 73的puppeteer 1.14(我不使用Chromium)
我用来关闭浏览器的代码

async close_browser() {
    for (let page of await this.browser.pages()) {
      await page.close({
        "runBeforeUnload": true
      });
    }
   await this.browser.close();
}
此页面是否有帮助?
0 / 5 - 0 等级

相关问题

octasimo picture octasimo  ·  3评论

bricss picture bricss  ·  3评论

namma-geniee picture namma-geniee  ·  3评论

selfrefactor picture selfrefactor  ·  3评论

historylife picture historylife  ·  3评论