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

> Learn how to use waitForFunction in ego-browser to create custom polling conditions. This article explains how to evaluate JavaScript until a truthy value or timeout is reached.

- Repository: [CitroLabs/ego-lite](https://github.com/citrolabs/ego-lite)
- Tags: how-to-guide
- Published: 2026-07-27

---

**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`](https://github.com/citrolabs/ego-lite/blob/main/src/helpers.ts) and implemented in [`src/driver/waits.ts`](https://github.com/citrolabs/ego-lite/blob/main/src/driver/waits.ts).

## Understanding the waitForFunction Architecture

The `waitForFunction` utility is implemented inside [`src/driver/waits.ts`](https://github.com/citrolabs/ego-lite/blob/main/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`](https://github.com/citrolabs/ego-lite/blob/main/src/helpers.ts) to make it available for import in agent scripts.

According to the source code in [`src/helpers.ts`](https://github.com/citrolabs/ego-lite/blob/main/src/helpers.ts), the function signature is:

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

```

The helper relies on [`src/state.ts`](https://github.com/citrolabs/ego-lite/blob/main/src/state.ts) for timing utilities (`state.sleep`, `state.now`, and default timeout values) and [`src/cdp-eval.ts`](https://github.com/citrolabs/ego-lite/blob/main/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`](https://github.com/citrolabs/ego-lite/blob/main/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:

```javascript
// 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:

```javascript
// 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:

```javascript
// 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:

```javascript
// 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`](https://github.com/citrolabs/ego-lite/blob/main/src/driver/waits.ts) and is exposed via [`src/helpers.ts`](https://github.com/citrolabs/ego-lite/blob/main/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`](https://github.com/citrolabs/ego-lite/blob/main/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.