ruby 41 lines · 5 steps

A thread-safe debouncer in Ruby

Collect rapid calls into one batch and flush them after a quiet period, using a mutex and a resettable timer thread.

Explained by highlit
1class Debouncer
2 def initialize(delay:)
3 @delay = delay
4 @mutex = Mutex.new
5 @pending = []
6 @timer = nil
7 end
8 
9 def call(item, &flush)
10 @mutex.synchronize do
11 @pending << item
12 @flush = flush
13 schedule
14 end
15 end
16 
17 private
18 
19 def schedule
20 @timer&.kill
21 @timer = Thread.new do
22 sleep @delay
23 flush_now
24 end
25 end
26 
27 def flush_now
28 batch = nil
29 handler = nil
30 @mutex.synchronize do
31 return if @pending.empty?
32 batch = @pending
33 handler = @flush
34 @pending = []
35 @timer = nil
36 end
37 handler.call(batch)
38 rescue => e
39 warn "[Debouncer] flush failed: #{e.message}"
40 end
41end
01 / 01
STEP 01

Walkthrough

Space play step click any line
Three takeaways
  1. 1Debouncing coalesces bursts of events into a single delayed action by resetting a timer on every new call.
  2. 2A mutex guards shared state so concurrent callers and the timer thread never see a half-updated batch.
  3. 3Draining state under the lock and running the handler outside it keeps the critical section short and avoids holding the lock during slow work.

Related explainers

Share this explainer

Here's the card — post it anywhere.

A thread-safe debouncer in Ruby — share card
Made with highlit — turn any snippet into a walkthrough like this in about a minute.
Explain your code