Beri tahu kami tentang lingkungan Anda:
Langkah apa yang akan mereproduksi masalah?
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();
Apa hasil yang diharapkan?
Count: 0
Open pages: 4
Open pages: 3
Open pages: 2
Open pages: 1
Open pages: 0
Closing empty browser
Browser closed
Apa yang terjadi?
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
Ini tidak terjadi setiap lari tetapi sering terjadi. Sangat mungkin saya melakukan sesuatu _bad_ di sini!
+1
Melakukan sedikit debug cepat, peristiwa targetDestroyed
dijalankan sebelum page.close()
pesan diakui (atau lebih tepatnya, dalang telah memproses pengakuan).
Saya dapat memikirkan cara untuk memperbaikinya - misalnya menunggu pengakuan Target.closeTarget
sebelum menangani peristiwa targetDestroyed
. Bagaimana cara melakukannya dengan benar adalah pertanyaan lain!
Terima kasih @MJPA atas penyelidikannya.
Masalahnya berasal dari fakta bahwa browser
menghapus target dari daftar sebelum halaman terkait ditutup.
Dalam contoh berikut, saya berharap browser.pages()
melaporkan 1 halaman hingga panggilan page.close()
selesai:
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();
})();
Hasil:
closing page...
Target destroyed. Pages count: 0
page closed.
@JoelEinbinder bagaimana menurut Anda?
Masalah yang sama seperti @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()
]);
Dalam kasus ini, selalu terjadi kesalahan yang sama.
Mungkin ini kesalahan dalang.
Saya mendapatkan kesalahan yang sama dengan implementasi yang sedikit mirip, tetapi dengan mendengarkan acara close
pada page
. Dokumentasi tidak terlalu eksplisit tentang apa artinya "tutup", tetapi menurut implementasinya, close
dipicu _before_ halaman ditutup, jadi contoh berikut tidak berfungsi, mirip dengan targetdestroyed
contoh.
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());
Saya kira _sortir_ ini masuk akal, karena acara tersebut disebut close
dan bukan closed
. Saya mengatasi ini sendiri dengan memancarkan acara closed
saya sendiri di callback close()
janji dan mendengarkannya sebagai gantinya, yang _ tidak berhasil.
page.close().then(() => {
page.emit('closed');
});
Moral dari cerita ini tampaknya tidak ada metode yang dijamin 100% untuk mengetahui kapan browser aman untuk ditutup (tanpa harus menangkap pengecualian). Tolong beri tahu saya jika saya melewatkan sesuatu.
Mungkin terkait dengan # 3423?
Saya mendapatkan ini setelah menutup semua tab di browser dan kemudian mencoba membuka halaman baru
Dalam contoh berikut, saya berharap
browser.pages()
melaporkan 1 halaman hingga panggilanpage.close()
selesai:
Ini akan memecahkan kode pada contoh di atas. Halaman terakhir ditutup, brower.pages (). Length akan menjadi 1, browser tidak akan ditutup. Maka tidak ada lagi peristiwa penghancuran target yang akan datang dan browser akan hidup selamanya.
Saya akan mengatakan ini adalah kesalahan pengguna, meskipun tidak kentara. Setiap panggilan ke page.close
harus ditunggu sebelum menelepon browser.close()
.
Jika tidak ada halaman yang dikontrol pengguna, cukup Promise. Semua halaman akan ditutup, lalu tutup browser. Seharusnya tidak perlu mendengarkan acara targetdestroyed
. Atau cukup panggil browser.close dan biarkan semua halaman menutup sendiri.
Jika ada halaman yang dikontrol pengguna, maka dengarkan acara targetdestroyed
, tetapi seharusnya tidak perlu memanggil page.close
.
+1.
Saya mulai melihat kesalahan ini setelah menutup popup dengan page.on("popup")
. Saat popup menyala di dalam konteks Frame terkadang saya tidak bisa menutupnya dengan "popup.close ()" dan dalang membiarkan tab terbuka, jadi saat saya mencoba menutup seluruh browser, muncul error "Target Closed", dan skrip berhenti.
Saya menggunakan dalang 1.14, dengan Chrome 73 (saya tidak menggunakan Chromium)
Kode yang saya gunakan untuk menutup browser
async close_browser() {
for (let page of await this.browser.pages()) {
await page.close({
"runBeforeUnload": true
});
}
await this.browser.close();
}