Pipo
Pipo

Reputation: 5599

How to wait N seconds before continuing to the next line?

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

Answers (8)

ggorlen
ggorlen

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:

  • Are you waiting for a CSS selector, aria label, text or XPath to appear? Try waitForSelector (optionally with the appropriate built-in selector).
  • Are you waiting for a particular URL? Try waitForFunction.
  • Are you waiting for a navigation? Try waitForNavigation or adjust the waitUntil option on goto.
  • Are you waiting for a network event or state? Try waitForRequest, waitForResponse or waitForNetworkIdle.
  • Are you waiting for a popup? Try promisifying page.on("dialog", ....
  • Are you waiting for some arbitrary predicate, like a minimum number of child elements to appear? Try waitForFunction.
  • Something else? Run an 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

Matheus Pinheiro
Matheus Pinheiro

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

Huckleberry Carignan
Huckleberry Carignan

Reputation: 2318

I've been using:

await page.waitForTimeout(3000);

Where 3000 is Milliseconds And that seems to be working for me.

Upvotes: 127

Md. Abu Taher
Md. Abu Taher

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

MatthewThomas.dev
MatthewThomas.dev

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

Yordan
Yordan

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

Xin
Xin

Reputation: 36520

await new Promise(_func=> setTimeout(_func, 5000));

Upvotes: -1

Grant Miller
Grant Miller

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

Related Questions