How to Use waitForFunction for Custom Polling Conditions in ego-browser

waitForFunction is a low-level helper that repeatedly evaluates JavaScript in the page context until it returns a truthy value or a timeout expires.

In the citrolabs/ego-lite repository, waitForFunction provides agents with a flexible mechanism to pause execution until custom conditions—such as DOM state changes, variable availability, or data loading—are met. This guide explains how to implement custom polling logic using the function exported from src/helpers.ts and implemented in src/driver/waits.ts.

Understanding the waitForFunction Architecture

The waitForFunction utility is implemented inside src/driver/waits.ts, where it constructs a Chrome DevTools Protocol (CDP) expression and manages the polling lifecycle. It is re-exported through src/helpers.ts to make it available for import in agent scripts.

According to the source code in src/helpers.ts, the function signature is:

waitForFunction(pageFunction, argOrOptions?, options?)   // → Promise<unknown|false>

The helper relies on src/state.ts for timing utilities (state.sleep, state.now, and default timeout values) and src/cdp-eval.ts to dispatch Runtime.evaluate calls to the browser.

How the Polling Loop Works

The internal polling mechanism follows a tight evaluation cycle defined in src/driver/waits.ts. When you invoke waitForFunction, the utility:

  1. Builds a CDP Runtime.evaluate expression using buildWaitForFunctionExpression, wrapping your supplied function or string for execution in the page context.
  2. Sends the expression via cdp("Runtime.evaluate", …) and reads the result using runtimeValue.
  3. Resolves the promise immediately if the returned value is truthy.
  4. Otherwise, sleeps for the configured polling duration using state.sleep(polling) and repeats until the deadline (state.now() + timeout) is reached.

This loop continues until the condition is satisfied or the timeout expires, at which point it returns false.

Function Signature and Options

The waitForFunction method accepts flexible arguments to accommodate both simple and complex polling scenarios:

  • pageFunction: A function or string expression to evaluate in the browser context. Must return a truthy value to indicate success.
  • argOrOptions: Either an argument to pass into pageFunction (if pageFunction accepts parameters) or an options object if no argument is needed.
  • options: An object configuring the wait behavior:
    • timeout (number): Maximum time to wait in milliseconds. Defaults to state.defaultTimeout.
    • polling (number): Interval between evaluations in milliseconds. Defaults to 100 ms.

Practical Code Examples

Waiting for Page Variables

Poll until a global JavaScript variable becomes available and truthy:

// Wait until the page sets window.isReady to true
const result = await waitForFunction(() => window.isReady);
console.log('Ready?', !!result);

Polling with Function Arguments

Pass dynamic data into the evaluated function to check for specific content:

// Wait until a specific element's text equals the target string
const target = 'Done';
const result = await waitForFunction(
  (t) => document.querySelector('#status')?.textContent?.trim() === t,
  target,                // argument passed to the function
  { timeout: 5000, polling: 200 } // custom options
);
if (result) console.log('Status reached:', target);

Using String Expressions

Supply a string expression instead of a function for simpler conditions:

// Equivalent to the previous example, but with a string expression
await waitForFunction(
  "document.querySelector('#status')?.textContent?.trim() === 'Done'",
  { timeout: 5000, polling: 200 }
);

Extracting Data from the Page

Use waitForFunction to retrieve computed values once elements are present:

// Retrieve the number of rows in a table once the table is present
const rowCount = await waitForFunction(() => {
  const table = document.querySelector('#myTable');
  return table ? table.rows.length : false;
});
console.log('Rows:', rowCount);

Summary

  • waitForFunction is the primary mechanism in ego-browser for implementing custom polling conditions against page state.
  • The implementation resides in src/driver/waits.ts and is exposed via src/helpers.ts, utilizing CDP Runtime.evaluate for execution.
  • The polling loop checks conditions every 100 ms by default, configurable via the polling option, and respects a timeout deadline.
  • You can pass arguments to page functions, use string expressions, and extract return values directly from the browser context.

Frequently Asked Questions

What is the default polling interval for waitForFunction?

The default polling interval is 100 milliseconds. You can override this by providing a polling value in the options object, as implemented in the polling loop within src/driver/waits.ts.

How does waitForFunction handle timeouts?

If the evaluated condition does not return a truthy value before the timeout duration elapses (defaulting to state.defaultTimeout), the function resolves to false. The deadline is calculated as state.now() + timeout and checked during each iteration of the polling loop.

Can I pass multiple arguments to the page function?

The current signature supports a single argument via the argOrOptions parameter. For multiple values, wrap them in an array or object and destructure them inside your page function.

What is the difference between waitForFunction and waitForSelector?

waitForSelector waits for a specific DOM element to appear, while waitForFunction evaluates arbitrary JavaScript expressions. Use waitForFunction when you need to check complex conditions like text content, variable state, or computed properties that go beyond element presence.

Have a question about this repo?

These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:

Share the following with your agent to get started:
curl -s "https://instagit.com/install.md"

Works with
Claude Codex Cursor VS Code OpenClaw Any MCP Client

Maintain an open-source project? Get it listed too →