2017-12-04 2 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++ 
    } 

pagetempPage 두 개의 다른 페이지입니다.

어떤 일이 발생하면 page이 10 초 동안 기다린 다음 몇 가지 사항을 클릭하면 두 번째 페이지가 열립니다.

무엇을해야할까요? tempPage은 요소를 기다렸다가 클릭 한 다음 페이지가 다시 시작되기 전에 10 초 동안 기다려야합니다.

실제로 실제로 발생하는 현상은 page이 10 초 동안 대기하고 물건을 클릭 한 다음 tempPage이 작업을 마칠 때까지 기다리지 않고 10 초 동안 기다리는 것입니다.

버그입니까? 아니면 오해입니까? for 루프가 다시 반복 될 때 tempPage이 (가) 클릭 한 후에 만이를 처리 할 수 ​​있도록 어떻게 수정해야합니까?

답변

5

일반적으로, 당신은 "[에디션]의 작업을 완료"한 tempPage 때까지 실행을 일시 중지 할 await tempPage.click("a.orange")에 의존 할 수 없다. 동기식으로 실행되는 슈퍼 간단한 코드의 경우 작동 할 수 있습니다. 그러나 일반적으로, 당신은 그것에 의지 할 수 없습니다.

클릭이 Ajax 연산을 트리거하거나 CSS 애니메이션을 시작하거나 즉시 계산할 수없는 계산을 시작하거나 새 페이지를 열면 대기중인 결과는 비동기이며 .click 메서드는이 비동기 작업이 완료 될 때까지 대기하지 않습니다.

무엇을 할 수 있습니까? 어떤 경우에는 페이지에서 실행중인 코드에 연결하여 중요한 이벤트가 발생할 때까지 기다릴 수 있습니다. 예를 들어 Ajax 작업이 완료 될 때까지 기다리고 페이지의 코드가 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 코드 클릭 시작 비동기 작업의 종류를 시뮬레이션 : 여기

문제를 보여주는 예이다. 이 코드를 실행하면

, 당신은 콘솔에서 볼 수 있습니다 :

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을 실행하는 노드와 브라우저를 제어하는 ​​스크립트가 있다는 것입니다. 브라우저 자체에는 자체 JavaScript 가상 머신이 있습니다. 노드 쪽에서 사용하는 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 전에 정의되어 있지만거야 button 때문에 page.evaluate 실행은 브라우저 측에 정의되어 있지 않은 경우 ReferenceError!