Skip to main content

Debouncing

Open in ChatGPT
25.11
Java API

Debouncing is a technique that delays executing an action until a specified time has elapsed since the last call. Each new call resets the timer. This is useful for scenarios like search-as-you-type, where you want to wait until the user stops typing before executing a search query.

Show Code

Basic usage

The Debouncer class provides a simple way to debounce actions. Create a Debouncer with a delay in seconds, then call run() with the action you want to debounce:

Debouncer debounce = new Debouncer(0.3f);

textField.onModify(e -> {
debounce.run(() -> search(textField.getText()));
});

In this example, the search() method is called only after the user stops typing for 300 milliseconds. Each keystroke resets the timer via the onModify event, so rapid typing won't trigger multiple searches.

How it works

When you call run() with an action:

  1. If no action is pending, the Debouncer schedules the action to run after the delay
  2. If an action is already pending, the previous action is cancelled and the timer restarts with the new action
  3. Once the delay elapses without another call, the action executes

The Debouncer runs on the UI thread using webforJ's Interval mechanism, so you don't need to wrap UI updates in Environment.runLater().

Delay units

The delay parameter uses seconds as the unit, not milliseconds. Use 0.3f for 300 ms or 1.5f for 1.5 seconds.

Controlling execution

The following methods can be used to more precisely handle the execution and use of the Debouncer:

Cancelling a pending action

Use cancel() to stop a pending action from executing:

Debouncer debounce = new Debouncer(1f);

debounce.run(() -> saveDocument());

// User navigates away before save executes
debounce.cancel();
Cancelling pending debounces

Like intervals, it's good practice to cancel pending debounced actions when a component is destroyed. This prevents memory leaks and avoids errors from actions executing on destroyed components:

public class SearchPanel extends Composite<Div> {
private final Debouncer debounce = new Debouncer(0.3f);

@Override
protected void onDidDestroy() {
debounce.cancel();
}
}

Forcing immediate execution

Use flush() to execute a pending action immediately:

Debouncer debounce = new Debouncer(0.5f);

textField.onModify(e -> {
debounce.run(() -> validateInput(textField.getText()));
});

// Force validation before form submission
submitButton.onClick(e -> {
debounce.flush();
if (isValid()) {
submitForm();
}
});

Checking pending status

Use isPending() to verify if an action is waiting to execute:

Debouncer debounce = new Debouncer(0.3f);

if (debounce.isPending()) {
statusLabel.setText("Processing...");
}

Event level debouncing vs Debouncer

webforJ provides two approaches to debouncing:

FeatureDebouncerElementEventOptions.setDebounce()
ScopeAny actionElement events only
LocationServer-sideClient-side
UnitSeconds (float)Milliseconds (int)
FlexibilityFull control with cancel/flushAutomatic with event

Use Debouncer when you need programmatic control over debouncing, such as cancelling or flushing pending actions. Use ElementEventOptions when you want simple client-side debouncing for element events without additional server round-trips.

// Using ElementEventOptions for client-side debouncing
ElementEventOptions options = new ElementEventOptions();
options.setDebounce(300);

element.addEventListener("input", e -> {
// This handler is debounced on the client
}, options);