I have the code below, and I'm trying to work with if
/else if
.
The thing is, I'm having issues detecting which function appeared on page and proceed with correct steps. Actually, it detects the function inside of the first if
when it appears, obviously, but when the function in the second if
appears, it doesn't detect it, or waits forever for first function only, then crashes for function timeout.
What should I do to my script to detect which one appeared and then to proceed with the correct steps to each detected function? Only one of them appears per attempt.
if (await page.waitForFunction('document.querySelector("body").innerText.includes("Configurando sua conta...")')){
console.log('Function 1 appeared, doing steps for function 1 (below)');
await page.waitForNavigation();
await page.waitForTimeout(100000);
await page.goto('website-secret');
} else if (await page.ForFunction('document.querySelector("body").innerText.includes("Você não está qualificado")')){
console.log('Function 2 appeared, doing steps for function 2 (below)')
await page.waitForTimeout(1200000);
await page.goto('website-secret');
}
There are a few ways.
The simplest way is to use a try
to catch the error raised when one element isn't found, then move on to the next. This makes sense when you want to prioritize one over the other:
try {
await page.waitForFunction("condition 1");
// condition 1 logic
}
catch (err) {
await page.waitForFunction("condition 2");
// condition 2 logic
}
Using conditions for control flow is ugly, and you might need to nest further if you're worried about either condition logic throwing (almost all Puppeteer funcs can throw).
A probably better approach was mentioned in the comments: Promise.race
, which accepts an array of promises and returns when at least one resolves. Here's an example:
const assert = require("node:assert/strict");
const puppeteer = require("puppeteer"); // ^14.3.0
const html = `
<body>
<p></p>
<script>
setTimeout(() => {
document.querySelector("p").textContent = ~~(Math.random() * 2);
}, 2000);
</script>
</body>
`;
let browser;
(async () => {
browser = await puppeteer.launch({headless: true});
const [page] = await browser.pages();
const query = "document.querySelector('p')?.textContent === ";
const conditions = [
page.waitForFunction(query + "'0'"),
page.waitForFunction(query + "'1'"),
].map(async (promise, index) => {
const result = await promise;
return {result, index};
});
const fastestCondition = Promise.race(conditions);
await page.setContent(html);
const fastestResult = await fastestCondition;
const pText = await page.$eval("p", el => el.textContent);
assert(fastestResult.index === +pText);
console.log(pText); // => sometimes 0, sometimes 1
})()
.catch(err => console.error(err))
.finally(() => browser?.close())
;
This code will throw if either promise rejects.
The index mapping is used to determine which promise succeeded in the main line, but you can also chain all dependent code after each waitFor...
if you never need to rejoin to the main line again.
You can also use allSettled
to figure out which promises rejected and which didn't.