Siirry pääsisältöön
← Blog

Debounce input in Java: search as you type with webforJ

· 5 minuuttia
Matthew Hawkins
Software Developer

cover image

Type "Amsterdam" into a search box that queries a server, and the server sees nine searches, one for each letter, all the way to the full word. Only the last one matters, but every one of them costs a request, and the results flicker as each answer arrives.

The fix is to debounce input in Java: wait until the user pauses, then search once with the text they settled on. webforJ has a Debouncer for exactly this, and it takes a few lines in your view. You write no timer or thread code of your own.

Try it live

The debouncing documentation has a live sample you can type into in the browser, with the full Java source next to it.

In this post you'll see why a search should wait, add the debouncer, clean it up when the view closes, and use the controls for pending work.

Why a search should wait​

Every keystroke fires an input event. A search wired straight to that event runs once for every character the user types. If the search calls a database or a REST service, each run is a request, and most of those requests return results nobody will read.

Debouncing changes when the action runs. Each new event resets a timer, and the action runs only after the timer finishes without another event arriving. The user types "Amsterdam" and the search runs once, after the last letter.

Delay search until typing stops​

A search wired straight to the field's onModify event runs once per keystroke. Add a Debouncer and wrap the call in run(). The constructor takes the delay in seconds as a float, so 0.3f means 300 milliseconds. This is the example from the docs:

Debouncer debounce = new Debouncer(0.3f);

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

Each call to run() replaces the pending action and restarts the timer, so typing "Ams" and then "Amsterdam" runs the search once, with the final text. The search reads the field when the timer fires, not when the key is pressed, so the query is always the text the user has settled on.

Cancel pending work on destroy​

If the user leaves the view within 300 milliseconds of typing, the pending search would still fire against a view that no longer exists. Cancel it when the component is destroyed, as the docs show:

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

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

The same pattern applies to any Debouncer you keep in a component.

Flush and check pending work​

Two more methods give you control over a pending action.

flush() runs the pending action immediately. Use it when a step needs the current input before it continues, such as a submit button that should validate the field before it sends the form.

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();
}
});

isPending() returns true while an action is waiting to run. You can use it to show a "searching" label while the timer runs.

To recap the three methods: run() schedules or replaces the pending action, cancel() drops it, and flush() runs it now. Together with isPending(), they cover everything a search box needs, so you don't have to write timer code yourself.

The delay units trip people up, so check them once. The Debouncer constructor takes seconds, so 0.3f is 300 milliseconds and 1.5f is a second and a half. Passing 300 would wait five minutes, which is rarely what you want. If your search feels slow, lower the number. If it fires while the user is still typing, raise it a little and try again.

Choosing between debounce options​

webforJ also offers ElementEventOptions.setDebounce(), which debounces element events in the browser and takes milliseconds. It works well when you only need to delay an element event and don't need to cancel or flush anything. Reach for Debouncer when the action runs on the server and you want to cancel or flush it, as in the preceding search example.

Next steps​

Add the DebouncedSearchView class to an app you already have, then open it at /debounced-search and type a few letters. If you are starting from scratch, startforJ gives you a project to paste it into. The Debouncing page covers the full API, and the TextArea page covers the output component.

Frequently asked questions​

How long should the delay be?​

The delay is up to you. The Debouncing page uses 0.3f for search and 0.5f for input validation, so start with one of those. Shorten it if the results feel slow to appear, and lengthen it if the search fires while the user is still typing.

Does the debounced action run on the UI thread?​

Yes. The Debouncer runs on the UI thread through webforJ's Interval mechanism, so you can update components inside the action without Environment.runLater(). Code that runs off the UI thread still needs asynchronous updates.

Is there a Java UI debouncer in webforJ?​

Yes. Debouncer is in the com.webforj package and has been available since webforJ 25.11. It works with any action in a webforJ view, and the search in this post is one example.

Rakennatko jotain webforJ:llä?

Keskustele tiimin kanssa projektistasi, migraatiosta tai siitä, sopiiko webforJ sinulle.

Ota yhteyttä