Reputation: 5599
In puppeteer I would like to wait a defined time before going to the next line of code.
I've tried to put a setTimeout
in an evaluate function but it seems to be simply ignored
console.log('before waiting');
await page.evaluate(async() => {
setTimeout(function(){
console.log('waiting');
}, 4000)
});
console.log('after waiting');
This code don't wait and just write before waiting and after waiting
Do you know how to do this?
Upvotes: 161
Views: 273470
Reputation: 56885
Other answers have shown how to sleep, but now that page.waitForTimeout
is finally deprecated in 16.1.1 and was removed in 22.0.0, I figure I'd add the answer I've wanted to add for awhile:
Don't sleep! It causes a race condition that disrupts the event-driven nature of Puppeteer, introducing unnecessary brittleness. There's almost always a better predicate to wait on, either explicitly or using the Locator API:
waitForSelector
(optionally with the appropriate built-in selector).waitForFunction
.waitForNavigation
or adjust the waitUntil
option on goto
.waitForRequest
, waitForResponse
or waitForNetworkIdle
.page.on("dialog", ...
.waitForFunction
.evaluate
block and add your own code to wait for a DOM mutation or poll with setInterval
or requestAnimationFrame
and effectively reimplement waitForFunction
as fits your needs.waitForFunction
in particular is underused but it adds a huge amount of reliability and precision that waitForTimeout
doesn't.
If all else fails and you have to block the script, you can delay, but I've written hundreds of Puppeteer scripts and have never had to use any sleep variant, so I'm pretty convinced it's basically unnecessary.
See my blog post on Puppeteer Antipatterns for more analysis of why you should avoid sleeping in any form except as an absolute last resort.
See the Puppeteer docs:
Puppeteer has event-driven architecture, which removes a lot of potential flakiness. There’s no need for evil “sleep(1000)” calls in puppeteer scripts.
See also the Playwright docs for waitForTimeout
, which is essentially the same as the Puppeteer method:
Discouraged
Never wait for timeout in production. Tests that wait for time are inherently flaky. Use Locator actions and web assertions that wait automatically.
Caveats to the above: sleeping can be useful for temporary debugging while developing a script, and for throttling requests to avoid overwhelming a server. But when using sleep throttling, make sure the script would work in an event-driven way without the sleeps. Using a sloMo
setting is generally better.
Now that you've been warned, see this answer for the correct Node timeout code.
Upvotes: 22
Reputation: 9
If the function is asynchronous (async function delay(time)), you can use await when calling this function to wait until the Promise is resolved before continuing to execute the code. This is useful in situations where you want to pause code execution until the Promise is resolved.
async function delay(time) {
return await new Promise(resolve => setTimeout(resolve, time));}
Upvotes: 0
Reputation: 2318
I've been using:
await page.waitForTimeout(3000);
Where 3000 is Milliseconds And that seems to be working for me.
Upvotes: 127
Reputation: 18826
You can use a little promise function,
function delay(time) {
return new Promise(function(resolve) {
setTimeout(resolve, time)
});
}
Then, call it whenever you want a delay.
console.log('before waiting');
await delay(4000);
console.log('after waiting');
If you must use puppeteer use the builtin waitForTimeout function.
await page.waitForTimeout(4000)
If you still want to use page.evaluate, resolve it after 4 seconds. You are not resolving anything.
await page.evaluate(async() => {
await new Promise(function(resolve) {
setTimeout(resolve, 1000)
});
});
But I guess you can simply use the first two examples.
Upvotes: 255
Reputation: 1045
page.waitFor has now been deprecated.
page.waitForTimeout is now advised to pause script execution for the given number of milliseconds before continuing:
await page.waitForTimeout(1000)
Upvotes: 27
Reputation: 131
Try this function.
function sleep(ms) {
return new Promise(resolve => setTimeout(resolve, ms));
}
to use it
async function demo() {
console.log('Waiting...');
await sleep(3000);
console.log('ok');
}
demo();
Upvotes: 4
Reputation: 28999
You can use one of the following options to wait for one second:
await page.waitFor(1000);
await frame.waitFor(1000);
await new Promise(r => setTimeout(r, 1000));
Alternatively, there are many Puppeteer functions that include a built-in delay
option, which may come in handy for waiting between certain events:
// Click Delay
// Time to wait between mousedown and mouseup in milliseconds. Defaults to 0.
await page.click('#example', {delay: 1000});
await frame.click('#example', {delay: 1000});
await elementHandle.click({delay: 1000});
await page.mouse.click(0, 0, {delay: 1000});
// Type Delay
// Time to wait between key presses in milliseconds. Defaults to 0.
await page.type('#example', 'Hello, world!', {delay: 1000});
await frame.type('#example', 'Hello, world!', {delay: 1000});
await elementHandle.type('Hello, world!', {delay: 1000});
await page.keyboard.type('Hello, world!', {delay: 1000});
// Press Delay
// Time to wait between keydown and keyup in milliseconds. Defaults to 0.
await elementHandle.press('Backspace', {delay: 1000});
await page.keyboard.press('Backspace', {delay: 1000});
Upvotes: 65