Расскажите нам о своем окружении:
Какие шаги воспроизведут проблему?
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
Это не происходит при каждом запуске, но случается очень часто. Вполне возможно, что я здесь делаю что-то _ плохое!
+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();
}