What’s the right way to use waitForEvent in Playwright projects?

I’ve been working on a web automation project using Playwright (v1.30) on Windows 10 with Node.js. I need to properly handle asynchronous events, so I started using waitForEvent().

However, the behavior is inconsistent. Sometimes the script moves forward without capturing the event, and other times it stalls as if the event never occurred.

How can I correctly use waitForEvent() in Playwright to ensure reliable and consistent event handling?

The most common mistake with waitForEvent() is starting to listen after the event has already been triggered. If the event fires before Playwright begins waiting, it will never be captured.

The safest and most recommended pattern is to start waiting and triggering the action simultaneously using Promise.all():

const [event] = await Promise.all([
page.waitForEvent('download'),
page.click('#download-button')
]);

This ensures:

The listener is attached before the action runs.

The event cannot be missed due to timing issues.

Always confirm:

You are using the correct event name (download, popup, request, etc.).

The action actually triggers that event.

You are waiting on the correct object (e.g., page, context, or browser).

Another reason scripts stall is that the event never fires. When this happens, waitForEvent() will wait indefinitely unless a timeout is specified.

You can add a timeout:

const event = await page.waitForEvent('popup', { timeout: 5000 });

If the event does not occur within 5 seconds, Playwright throws a timeout error — which is helpful for debugging.

Also, make sure you are listening on the correct object:

page.waitForEvent('popup') → for new tabs triggered by the page

context.waitForEvent('page') → for new pages in the browser context

page.waitForEvent('request') → for network requests

Listening on the wrong object is a common cause of missed events.