أخبرنا عن بيئتك:
ما هي الخطوات التي سوف تتكاثر هذه المشكلة؟
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()
بالإبلاغ عن صفحة واحدة حتى تنتهي المكالمة 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
_b قبل_ إغلاق الصفحة ، وبالتالي فإن المثال التالي لا يعمل ، على غرار 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
. لقد تمكنت من التغلب على هذا بمفردي عن طريق إصدار حدث closed
في رد الاتصال بالوعد close()
والاستماع إلى ذلك بدلاً من ذلك ، والذي _ يفعل_.
page.close().then(() => {
page.emit('closed');
});
يبدو أن المغزى من القصة هو أنه لا توجد طريقة مضمونة 100٪ لمعرفة متى يكون المتصفح آمنًا للإغلاق (دون الحاجة إلى اكتشاف الاستثناء). يرجى إعلامي إذا فاتني شيء ما.
يحتمل أن تكون مرتبطة بـ # 3423؟
أحصل على هذا بعد إغلاق جميع علامات التبويب في المتصفح ثم محاولة فتح صفحة جديدة
في المثال التالي ، أتوقع أن يقوم
browser.pages()
بالإبلاغ عن صفحة واحدة حتى تنتهي المكالمةpage.close()
:
هذا من شأنه كسر الكود في المثال أعلاه. الصفحة الأخيرة مغلقة ، brower.pages (). سيكون الطول 1 ، ولن يتم إغلاق المتصفح. ثم لن تأتي المزيد من الأحداث المستهدفة المدمرة وسيظل المتصفح يعيش إلى الأبد.
أود أن أقول أن هذا خطأ مستخدم ، على الرغم من كونه دقيقًا. يجب انتظار أي مكالمات إلى 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();
}