Puppeteer: Закрытие браузера при закрытии последней страницы иногда вызывает ошибку

Созданный на 27 мар. 2018  ·  9Комментарии  ·  Источник: puppeteer/puppeteer

Расскажите нам о своем окружении:

  • Версия кукольника: 1.2.0
  • Платформа / версия ОС: CentOS 7.4.1708
  • URL-адреса (если применимо): N / A
  • Версия 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

Это не происходит при каждом запуске, но случается очень часто. Вполне возможно, что я здесь делаю что-то _ плохое!

bug chromium

Все 9 Комментарий

+1

Выполнив небольшую быструю отладку, событие targetDestroyed запускается до того, как сообщение page.close() было подтверждено (или, скорее, кукловод обработал подтверждение).

Я могу придумать способы исправить это - например, дождаться подтверждения Target.closeTarget перед обработкой события targetDestroyed . Как это сделать - другой вопрос!

Спасибо @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()
    ]);

В этом случае всегда возникали одни и те же ошибки.

Может это ошибка кукловода.

Я получаю ту же ошибку с немного похожей реализацией, но при прослушивании события close в page . В документации не слишком четко указано, что означает «закрыть», но в соответствии с его реализацией close запускается _перед_ закрытием страницы, поэтому следующий пример не работает, аналогично targetdestroyed example.

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 . Я обошел это самостоятельно, выполнив собственное событие closed в обратном вызове close() обещания и послушав это вместо этого, что _действует_.

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

Мораль этой истории, похоже, заключается в том, что не существует 100% гарантированного способа узнать, когда браузер безопасно закрыть (без необходимости перехватывать исключение). Пожалуйста, дайте мне знать, если я что-то упускаю.

Возможно связано с № 3423?

Я получаю это после закрытия всех вкладок в браузере, а затем попытки открыть новую страницу

В следующем примере я ожидаю, что browser.pages() сообщит об 1 странице, пока вызов page.close() будет завершен:

Это нарушит код в приведенном выше примере. Последняя закрытая страница, brower.pages (). Length будет равна 1, браузер не будет закрыт. Тогда больше не будет событий об уничтожении цели, и браузер будет жить вечно.

Я бы сказал, что это ошибка пользователя, хотя и незаметная. Перед вызовом browser.close() следует дождаться любых вызовов page.close browser.close() .

Если страниц, управляемых пользователем, нет, просто Promise.all закроет все страницы, а затем закройте браузер. Не должно быть необходимости прослушивать события targetdestroyed . Или просто вызовите browser.close и позвольте всем страницам закрыться самостоятельно.

Если есть страницы, управляемые пользователем, то слушайте события targetdestroyed , но вызывать page.close не нужно.

+1.
Я начал видеть эту ошибку после закрытия всплывающих окон с помощью page.on("popup") . Когда всплывающее окно срабатывает внутри контекста фрейма, иногда я не могу закрыть его с помощью «popup.close ()», и кукольник оставляет вкладку открытой, поэтому, когда я пытаюсь закрыть весь браузер, он выдает ошибку «Цель закрыта», и сценарий остановится.

Я использую кукольник 1.14 с Chrome 73 (я не использую Chromium)
Код, который я использую для закрытия браузера

async close_browser() {
    for (let page of await this.browser.pages()) {
      await page.close({
        "runBeforeUnload": true
      });
    }
   await this.browser.close();
}
Была ли эта страница полезной?
0 / 5 - 0 рейтинги

Смежные вопросы

MehdiRaash picture MehdiRaash  ·  3Комментарии

barnash picture barnash  ·  3Комментарии

sradu picture sradu  ·  3Комментарии

kesava picture kesava  ·  3Комментарии

ryanvincent29 picture ryanvincent29  ·  3Комментарии