1

I want to write numbers from an array into an HTML input in sequence. The values of the array inside my for..of loop return undefined only in the asynchronous operations. Here's my code:

const puppeteer = require('puppeteer');
const years = [2000,2001,2002];

(async () => {
    const browser = await puppeteer.launch({
        executablePath: './chromium/chrome.exe',
        headless:false,
        product:'chrome',
        userDataDir: './cache'
    });
    const page = (await browser.pages())[0];
    await page.setViewport({width:1920, height:1080});
    await page.goto("https://www.immotop.lu/search/");

    const example = async ()=>  {
        for (year of years){
            await page.$eval('#search_form > div.filt-but-block > button.button-l3.filter-pan', el => el.click());
            await page.$eval('#year_build', el => el.value = "" + year) // year is undefined here
        }
    }
    example().then(() =>{
        console.log('done');
    })
})();

I think this is due to the synchronous looping through my array while executing asynchronous code with await, so the looping finished and the variable disappears before await is executed. Additionally I've heard that there should be a way to seperately save the variable in a function body, but so far I haven't figured out how to do it.

Here are the error messages:

(node:23492) UnhandledPromiseRejectionWarning: Error: Evaluation failed: 
ReferenceError: year is not defined
 (node:23492) 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(). To terminate the node process on unhandled promise rejection, use the CLI flag `--unhandled-rejections=strict` (see https://nodejs.org/api/cli.html#cli_unhandled_rejections_mode). (rejection id: 1)
    (node:23492) [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.
2
  • You missed the variable definiton in the loop yiu have to use let before year Commented Oct 24, 2020 at 16:07
  • Thank you, definetely got that fixed, but the same error remains. Commented Oct 24, 2020 at 18:16

2 Answers 2

1

When we use Puppeteer there are two separate javascript contexts:

  1. Node context, from which the script runs
  2. Browser context: a completely different javascript runtime that works in a Chromium - puppeteer only sends there functions and receives results of their evaluation.

Variables and functions that are declared in the node context do not exist in the browser and vice versa.

// NODE CONTEXT
// this script works in Node.js
const puppeteer = require('puppeteer');
const years = [2000,2001,2002];

(async () => {
    const browser = await puppeteer.launch();
    const page = (await browser.pages())[0];
    await page.goto("https://www.immotop.lu/search/");

    for (year of years) { // <-- year exists in node.js context
        // But all functions executed inside of evaluation methods,
        // like page.evaluate, page.$eval, page.$$eval
        // are actually run in the browser
        // and there is no "year" variable there
        await page.$eval('#search_form > div.filt-but-block > button.button-l3.filter-pan', el => el.click());
        await page.$eval('#year_build', el => el.value = "" + year) // year is undefined here
    }
})();

To fix this problem you need to send to the browser context not only a function el.value = "" + year, but also the value of the year. See the function signature in the docs:

page.$eval(selector, pageFunction[, ...args])

args is what we're after here. It means that after the function to execute we can pass as many additional arguments as necessary. The corrected code will look like this:

for (year of years) {
  const yearBuild = await page.$eval(
    "#year_build", // <-- element selector
    (el, yearFromNode) => (el.value = "" + yearFromNode), // function to run
    year // <-- variable from node context to send to the browser
  );
}
Sign up to request clarification or add additional context in comments.

1 Comment

Thank you very much! I understand the problem very well now.
0

You have not defined your iterator variable in the for..of loop, you just need to use the let keyword to define 'year' in the for..of loop

The reason this throws an error is probably because you might be using 'Strict Mode' with 'use strict;' at the very top of your .js file. Because while in strict mode, global variables throw exceptions in for..in or for..of loops like this one.

  for (let year of years){
                await page.$eval('#search_form > div.filt-but-block > button.button-l3.filter-pan', el => el.click());
                await page.$eval('#year_build', el => el.value = "" + year) // year is undefined here
                });
            }

1 Comment

Thank you, I used 'let' now and it still throws the same exception. I'm not using Strict Mode as far as I m aware, const puppeteer = require('puppeteer'); is the only line missing in my code from the original.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.