2017-12-04 18 views
10

तो यहाँ कोड स्निपेट से पहले उनके कार्यों खत्म करने के लिए एक और पेज के लिए प्रतीक्षा नहीं करता है:पेज जारी

for (let item of items) 
    { 
     await page.waitFor(10000) 
     await page.click("#item_"+item) 
     await page.click("#i"+item) 

     let pages = await browser.pages() 
     let tempPage = pages[pages.length-1] 

     await tempPage.waitFor("a.orange", {timeout: 60000, visible: true}) 
     await tempPage.click("a.orange") 

     counter++ 
    } 

page और tempPage दो भिन्न पृष्ठों हैं।

क्या होता है page 10 सेकंड तक प्रतीक्षा करता है, फिर कुछ सामान क्लिक करता है, जो एक दूसरा पृष्ठ खोलता है।

क्या होना चाहिए यह है कि tempPage किसी तत्व के लिए प्रतीक्षा करता है, इसे क्लिक करता है, फिर पेज को फिर से करने से पहले 10 सेकंड का इंतजार करना चाहिए।

हालांकि, क्या वास्तव में ऐसा होता है तो इसकी कार्य समाप्त करने के लिए tempPage का इंतजार किए बिना 10 सेकंड के लिए प्रतीक्षा कर रहा शुरू होता है, 10 सेकंड के लिए प्रतीक्षा करता है page, सामान क्लिक करता है।

क्या यह एक बग है, या क्या मैं कुछ गलत समझ रहा हूं? मुझे इसे कैसे ठीक करना चाहिए ताकि जब for लूप लूप फिर से हो, तो tempPage के बाद ही क्लिक किया गया है।

उत्तर

5

आम तौर पर, आप tempPage तक निष्पादन को रोकने के लिए "[एड] अपने कार्यों खत्म" है await tempPage.click("a.orange") पर भरोसा नहीं कर सकते हैं। सुपर सरल कोड के लिए जो सिंक्रनाइज़ रूप से निष्पादित करता है, यह काम कर सकता है। लेकिन सामान्य रूप से, आप इस पर भरोसा नहीं कर सकते हैं।

यदि क्लिक अजाक्स ऑपरेशन को ट्रिगर करता है, या सीएसएस एनीमेशन शुरू करता है, या एक गणना शुरू करता है जिसे तत्काल गणना नहीं की जा सकती है, या एक नया पेज खोलता है, तो परिणाम जिसके लिए आप प्रतीक्षा कर रहे हैं वह असीमित है, और .click विधि इस एसिंक्रोनस ऑपरेशन को पूरा करने के लिए इंतजार नहीं करेगी।

आप क्या कर सकते हैं? कुछ मामलों में आप पृष्ठ पर चल रहे कोड में हुक करने में सक्षम हो सकते हैं और आपके लिए महत्वपूर्ण कुछ घटनाओं की प्रतीक्षा कर सकते हैं। उदाहरण के लिए, यदि आप अजाक्स ऑपरेशन के लिए इंतजार करना चाहते हैं और पृष्ठ पर कोड jQuery का उपयोग करता है, तो आप ऑपरेशन पूर्ण होने पर पता लगाने के लिए ajaxComplete का उपयोग कर सकते हैं। यदि आप ऑपरेशन के दौरान पता लगाने के लिए किसी भी घटना प्रणाली में शामिल नहीं हो सकते हैं, तो ऑपरेशन पूरा होने के सबूत की प्रतीक्षा करने के लिए आपको पृष्ठ को मतदान करने की आवश्यकता हो सकती है।

const puppeteer = require('puppeteer'); 

function getResults(page) { 
    return page.evaluate(() => ({ 
     clicked: window.clicked, 
     asynchronousResponse: window.asynchronousResponse, 
    })); 
} 

puppeteer.launch().then(async browser => { 
    const page = await browser.newPage(); 
    await page.goto("https://example.com"); 
    // We add a button to the page that will click later. 
    await page.evaluate(() => { 
     const button = document.createElement("button"); 
     button.id = "myButton"; 
     button.textContent = "My Button"; 
     document.body.appendChild(button); 
     window.clicked = 0; 
     window.asynchronousResponse = 0; 
     button.addEventListener("click",() => { 
      // Synchronous operation 
      window.clicked++; 

      // Asynchronous operation. 
      setTimeout(() => { 
       window.asynchronousResponse++; 
      }, 1000); 
     }); 
    }); 

    console.log("before clicks", await getResults(page)); 

    const button = await page.$("#myButton"); 
    await button.click(); 
    await button.click(); 
    console.log("after clicks", await getResults(page)); 

    await page.waitForFunction(() => window.asynchronousResponse === 2); 
    console.log("after wait", await getResults(page)); 

    await browser.close(); 
}); 

setTimeout कोड क्लिक द्वारा शुरू अतुल्यकालिक आपरेशन के किसी भी प्रकार simulates:

यहाँ एक उदाहरण है कि इस मुद्दे को पता चलता है।

जब आप इस कोड को चलाने के लिए, आप कंसोल पर देखेंगे:

before click { clicked: 0, asynchronousResponse: 0 } 
after click { clicked: 2, asynchronousResponse: 0 } 
after wait { clicked: 2, asynchronousResponse: 2 } 

आप देखते हैं कि clicked तुरंत दो क्लिक द्वारा दो बार वृद्धि की जाती है। हालांकि, asynchronousResponse से पहले कुछ समय लगता है। बयान await page.waitForFunction(() => window.asynchronousResponse === 2) पृष्ठ को तब तक चुनाव करता है जब तक हम जिस स्थिति की प्रतीक्षा कर रहे हैं उसे महसूस नहीं किया जाता है।


आपने टिप्पणी में उल्लेख किया है कि बटन टैब बंद कर रहा है। खोलने और बंद करने वाले टैब एसिंक्रोनस ऑपरेशंस हैं।यहाँ एक उदाहरण है:

puppeteer.launch().then(async browser => { 
    let pages = await browser.pages(); 
    console.log("number of pages", pages.length); 
    const page = pages[0]; 
    await page.goto("https://example.com"); 
    await page.evaluate(() => { 
     window.open("https://example.com"); 
    }); 

    do { 
     pages = await browser.pages(); 
     // For whatever reason, I need to have this here otherwise 
     // browser.pages() always returns the same value. And the loop 
     // never terminates. 
     await page.evaluate(() => {}); 
     console.log("number of pages after evaluating open", pages.length); 
    } while (pages.length === 1); 

    let tempPage = pages[pages.length - 1]; 

    // Add a button that will close the page when we click it. 
    tempPage.evaluate(() => { 
     const button = document.createElement("button"); 
     button.id = "myButton"; 
     button.textContent = "My Button"; 
     document.body.appendChild(button); 
     window.clicked = 0; 
     window.asynchronousResponse = 0; 
     button.addEventListener("click",() => { 
      window.close(); 
     }); 
    }); 

    const button = await tempPage.$("#myButton"); 
    await button.click(); 

    do { 
     pages = await browser.pages(); 
     // For whatever reason, I need to have this here otherwise 
     // browser.pages() always returns the same value. And the loop 
     // never terminates. 
     await page.evaluate(() => {}); 
     console.log("number of pages after click", pages.length); 
    } while (pages.length > 1); 

    await browser.close(); 
}); 

जब मैं ऊपर चलाने के लिए, मैं:

number of pages 1 
number of pages after evaluating open 1 
number of pages after evaluating open 1 
number of pages after evaluating open 2 
number of pages after click 2 
number of pages after click 1 

आप इसे window.open() से पहले थोड़ा समय लगता है देख सकते हैं और पता लगाने योग्य window.close() प्रभाव है।


अपनी टिप्पणी में आप भी लिखा है:

मैंने सोचा था कि await क्या एक तुल्यकालिक एक

में एक अतुल्यकालिक समारोह बदल गया मैं यह नहीं कहूंगा कि यह तुल्यकालिक में अतुल्यकालिक कार्यों बदल जाता है मूल रूप से था लोगों को। यह वर्तमान कोड को एसिंक्रोनस ऑपरेशन के वादे को हल या अस्वीकार करने का इंतजार करता है। हालांकि, यहां इस मुद्दे के लिए और अधिक महत्वपूर्ण बात यह है कि समस्या यह है कि आपके पास जावास्क्रिप्ट कोड निष्पादित करने वाली दो आभासी मशीनें हैं: वहां नोड है जो puppeteer चलाता है और ब्राउज़र को नियंत्रित करने वाली स्क्रिप्ट है, और ब्राउज़र स्वयं ही जावास्क्रिप्ट वर्चुअल मशीन है। कोई भी await जो आप नोड पक्ष पर उपयोग करते हैं केवल नोड कोड को प्रभावित करता है: ब्राउज़र में चलने वाले कोड पर इसका कोई असर नहीं पड़ता है।

जब आप await page.evaluate(() => { some code; }) जैसी चीजें देखते हैं तो यह भ्रमित हो सकता है। ऐसा लगता है कि यह एक टुकड़ा है, और सभी एक ही वर्चुअल मशीन में निष्पादित हैं, लेकिन ऐसा नहीं है। puppeteer पैरामीटर को .evaluate पर पास किया गया है, इसे क्रमबद्ध करता है, और इसे ब्राउज़र पर भेजता है, जहां यह निष्पादित होता है। const button = ... के बाद उपरोक्त स्क्रिप्ट में await page.evaluate(() => { button.click(); }); जैसे कुछ जोड़ने का प्रयास करें। कुछ इस तरह:

const button = await tempPage.$("#myButton"); 
await button.click(); 
await page.evaluate(() => { button.click(); }); 

स्क्रिप्ट में, buttonpage.evaluate से पहले परिभाषित किया गया है, लेकिन आप मिल जाएगा एक ReferenceError जब क्योंकि buttonpage.evaluate रन ब्राउज़र पक्ष पर परिभाषित नहीं है!