Puppeteer: Menutup browser saat halaman terakhir ditutup terkadang menimbulkan kesalahan

Dibuat pada 27 Mar 2018  ·  9Komentar  ·  Sumber: puppeteer/puppeteer

Beri tahu kami tentang lingkungan Anda:

  • Versi dalang: 1.2.0
  • Versi platform / OS: CentOS 7.4.1708
  • URL (jika ada): T / A
  • Versi Node.js: v8.10.0

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!

bug chromium

Semua 9 komentar

+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 panggilan page.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();
}
Apakah halaman ini membantu?
0 / 5 - 0 peringkat