Debouncing and Batching Updates
DebouncedUpdates is a utility for batching events over a time window and processing them after a delay. It is a thin wrapper around a Kotlin channel that provides debouncing, optional deduplication, and testing utilities. It is the recommended replacement for the obsolete MergingUpdateQueue.
Typical use cases include:
Reacting to VFS or editor events — debounce notifications triggered by
BulkFileListeneror document changes, so that saving multiple files at once triggers only one updateSearch field debouncing — queue a search on every keystroke but process it only after the user pauses typing
Batching redraw or invalidation requests — collect multiple requests triggered in quick succession and process them together, optionally deduplicating by key
Deferring heavy work — postpone SDK detection, index rebuilds, or other expensive operations until a quiet moment
Creating a Queue
Use one of the factory methods to create a queue. The result must be stored as a field or property, as it is the handle used to queue items and check processing status in tests.
forScope
Ties the queue's lifecycle to a CoroutineScope. The queue stops processing when the scope is cancelled. This is the most common option when the queue's lifetime should match a service scope:
forComponent
Ties the queue's lifecycle to a Swing component. Processing is automatically paused when the component is not showing and resumed once it becomes visible again:
cancelOnDispose
Optionally, a queue created with forScope or forComponent can also be cancelled when a Disposable is disposed:
Execution Modes
runLatest
Executes only the most recent item queued during the delay window. Intermediate items are dropped. This is the most common mode, suitable for UI refresh, state sync, or search debouncing.
From Java, use .withContext(...) to specify the execution dispatcher explicitly:
runBatched
Collects all items queued during the delay window and delivers them as a List. Useful when all events must be processed, not just the last one:
runBatchedDistinct
Like runBatched, but deduplicates items by equality and delivers them as a Set:
Debounce vs. Throttle Mode
By default, the timer starts when the first item is queued (throttle mode — fixed interval regardless of new items). Setting .restartTimerOnAdd(true) switches to debounce mode — the timer resets on each new item, so processing happens only after a quiet period:
Testing
Key testing methods include:
queue.waitForAllExecuted(timeout)— suspends or blocks until all queued items are processed (use off EDT)queue.isAllExecuted— non-blocking check, suitable withPlatformTestUtil.waitWithEventsDispatching()
Migrating from MergingUpdateQueue
DebouncedUpdates covers the vast majority of MergingUpdateQueue use cases:
Updates with a fixed identity (e.g.,
Update.create("key") { ... }) — userunLatest, which keeps only the most recent itemUpdates without a fixed identity collected over time — use
runBatchedorrunBatchedDistinct, and handle any priority or merging logic in the batch handler
The only cases where MergingUpdateQueue should be kept are those requiring manual activation/suspension control via activate()/suspend()/resume().
Before ( | After ( |
|---|---|
|
|
|
|
|
|
|
|
|
|
|
|
See DebouncedUpdates KDoc for the full API reference.