2017-12-04 19 views
10

Vì vậy, đây là đoạn mã:trang không chờ đợi cho một trang khác để kết thúc nhiệm vụ của mình trước khi tiếp tục

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 hai trang khác nhau.

Điều gì xảy ra là page đợi trong 10 giây, sau đó nhấp vào một số nội dung sẽ mở trang thứ hai.

Điều đáng lẽ xảy ra là tempPage chờ phần tử, nhấp vào phần tử đó, sau đó trang sẽ đợi 10 giây trước khi thực hiện lại tất cả.

Tuy nhiên, điều thực sự xảy ra là page chờ 10 giây, nhấp vào nội dung, sau đó bắt đầu chờ 10 giây mà không cần đợi tempPage để hoàn thành nhiệm vụ của mình.

Đây có phải là lỗi hay tôi hiểu nhầm điều gì đó? Làm thế nào tôi nên sửa lỗi này để khi vòng lặp for lặp lại, nó chỉ sau khi tempPage đã được nhấp.

Trả lời

5

Thường là, bạn không thể dựa vào await tempPage.click("a.orange") để tạm dừng thực hiện cho đến khi tempPage có "hoàn thành [ed] nhiệm vụ của mình". Đối với mã siêu đơn giản thực hiện đồng bộ, nó có thể hoạt động. Nhưng nói chung, bạn không thể dựa vào nó.

Nếu nhấp chuột kích hoạt hoạt động Ajax hoặc bắt đầu hoạt ảnh CSS hoặc bắt đầu tính toán không thể tính ngay lập tức hoặc mở trang mới, v.v ... thì kết quả bạn đang chờ là không đồng bộ và .click phương thức này sẽ không đợi thao tác không đồng bộ này hoàn tất.

Bạn có thể làm gì? Trong một số trường hợp, bạn có thể móc vào mã đang chạy trên trang và đợi một số sự kiện quan trọng đối với bạn. Ví dụ, nếu bạn muốn đợi cho một thao tác Ajax được thực hiện và mã trên trang sử dụng jQuery, thì bạn có thể sử dụng ajaxComplete để phát hiện khi hoạt động hoàn tất. Nếu bạn không thể móc nối vào bất kỳ hệ thống sự kiện nào để phát hiện khi hoạt động được thực hiện, thì bạn có thể cần phải thăm dò trang để chờ bằng chứng cho thấy hoạt động được thực hiện.

Dưới đây là một ví dụ cho thấy các vấn đề:

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 mô phỏng bất kỳ loại hoạt động không đồng bộ bắt đầu bằng cách nhấp chuột.

Khi bạn chạy mã này, bạn sẽ thấy trên giao diện điều khiển:

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

Bạn thấy rằng clicked được ngay lập tức tăng lên gấp đôi bởi hai cú nhấp chuột. Tuy nhiên, phải mất một lúc trước khi asynchronousResponse được tăng lên. Tuyên bố await page.waitForFunction(() => window.asynchronousResponse === 2) thăm dò ý kiến ​​trang cho đến khi điều kiện chúng tôi chờ đợi được thực hiện.


Bạn đã đề cập trong nhận xét rằng nút đang đóng tab. Các tab mở và đóng là các hoạt động không đồng bộ.Dưới đây là một ví dụ:

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(); 
}); 

Khi tôi chạy ở trên, tôi nhận được:

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 

Bạn có thể nhìn thấy nó mất một chút trước khi window.open()window.close() có tác dụng phát hiện.


Trong bình luận của bạn, bạn cũng viết:

Tôi nghĩ await là cơ bản những gì đã biến một chức năng không đồng bộ thành đồng bộ một

Tôi sẽ không nói nó quay chức năng không đồng bộ vào đồng bộ những người. Nó làm cho mã hiện tại chờ đợi lời hứa của một hoạt động không đồng bộ được giải quyết hoặc bị từ chối. Tuy nhiên, quan trọng hơn cho vấn đề ở đây, vấn đề là bạn có hai máy ảo thực thi mã JavaScript: có Node chạy puppeteer và tập lệnh điều khiển trình duyệt và chính trình duyệt có máy ảo JavaScript của riêng nó. Bất kỳ await mà bạn sử dụng ở phía nút Node chỉ ảnh hưởng đến mã Node: nó không có ổ đĩa trên mã chạy trong trình duyệt.

Nó có thể gây nhầm lẫn khi bạn nhìn thấy những thứ như await page.evaluate(() => { some code; }). Dường như nó là tất cả của một phần, và tất cả thực hiện trong cùng một máy ảo, nhưng nó không phải là. puppeteer lấy tham số được chuyển đến .evaluate, tuần tự hóa nó và gửi nó tới trình duyệt, nơi nó thực thi. Thử thêm một cái gì đó như await page.evaluate(() => { button.click(); }); trong tập lệnh ở trên, sau const button = .... Một cái gì đó như thế này:

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

Trong kịch bản, button được định nghĩa trước page.evaluate, nhưng bạn sẽ nhận được một ReferenceError khi page.evaluate chạy vì button không được định nghĩa ở phía trình duyệt!

Các vấn đề liên quan