Class: Philiprehberger::Debounce::Debouncer
- Inherits:
-
Object
- Object
- Philiprehberger::Debounce::Debouncer
- Defined in:
- lib/philiprehberger/debounce/debouncer.rb
Overview
Delays execution until the wait period elapses without new calls.
When #call is invoked, any pending execution is cancelled and the timer restarts. The block only fires once the caller stops calling for the full wait duration.
Instance Method Summary collapse
-
#call(*args) ⇒ void
Invoke the debouncer with optional arguments.
-
#cancel ⇒ void
Cancel any pending execution.
-
#flush ⇒ void
Execute the pending block immediately and cancel the timer.
-
#initialize(wait:, leading: false, trailing: true, max_wait: nil, on_execute: nil, on_cancel: nil, on_flush: nil, on_error: nil, &block) ⇒ Debouncer
constructor
A new instance of Debouncer.
-
#last_result ⇒ Object?
Returns the result of the last block execution.
-
#metrics ⇒ Hash
Returns metrics about debouncer usage.
-
#pending? ⇒ Boolean
Whether there is a pending execution.
-
#pending_args ⇒ Array?
Returns the arguments that would be passed to the next execution.
-
#reset_metrics ⇒ void
Resets all metric counters to zero.
Constructor Details
#initialize(wait:, leading: false, trailing: true, max_wait: nil, on_execute: nil, on_cancel: nil, on_flush: nil, on_error: nil, &block) ⇒ Debouncer
Returns a new instance of Debouncer.
24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 |
# File 'lib/philiprehberger/debounce/debouncer.rb', line 24 def initialize(wait:, leading: false, trailing: true, max_wait: nil, on_execute: nil, on_cancel: nil, on_flush: nil, on_error: nil, &block) raise ArgumentError, 'block is required' unless block raise ArgumentError, 'wait must be positive' unless wait.positive? raise ArgumentError, 'at least one of leading or trailing must be true' if !leading && !trailing raise ArgumentError, 'max_wait must be positive' if max_wait && !max_wait.positive? @wait = wait @leading = leading @trailing = trailing @max_wait = max_wait @on_execute = on_execute @on_cancel = on_cancel @on_flush = on_flush @on_error = on_error @block = block @mutex = Mutex.new @pending = false @last_args = nil @called_leading = false @generation = 0 @call_count = 0 @execution_count = 0 @first_call_time = nil @last_result = nil end |
Instance Method Details
#call(*args) ⇒ void
This method returns an undefined value.
Invoke the debouncer with optional arguments.
Resets the internal timer. The block will execute after wait seconds of inactivity (trailing edge) or immediately on the first call (leading edge).
57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 |
# File 'lib/philiprehberger/debounce/debouncer.rb', line 57 def call(*args) @mutex.synchronize do @call_count += 1 @last_args = args @pending = true @generation += 1 current_gen = @generation @first_call_time ||= monotonic_now # Leading edge: fire immediately on the first call of a new cycle if @leading && !@called_leading @called_leading = true execute(args) end # Check if max_wait has been exceeded if @max_wait && @first_call_time && (monotonic_now - @first_call_time) >= @max_wait if @trailing args_to_use = @last_args @pending = false @last_args = nil @called_leading = false @first_call_time = nil execute(args_to_use) end return end # Start a new trailing timer if @trailing || @leading effective_wait = @wait if @max_wait && @first_call_time remaining_max = @max_wait - (monotonic_now - @first_call_time) effective_wait = [effective_wait, remaining_max].min if remaining_max.positive? end Thread.new do sleep effective_wait @mutex.synchronize do # Only fire if no new calls happened since this timer started if @generation == current_gen && @pending if @trailing args_to_use = @last_args @pending = false @last_args = nil @called_leading = false @first_call_time = nil execute(args_to_use) else @pending = false @called_leading = false @first_call_time = nil end end end end end end end |
#cancel ⇒ void
This method returns an undefined value.
Cancel any pending execution.
122 123 124 125 126 127 128 129 130 131 132 |
# File 'lib/philiprehberger/debounce/debouncer.rb', line 122 def cancel @mutex.synchronize do @generation += 1 @pending = false @last_args = nil @called_leading = false @first_call_time = nil end invoke_callback(@on_cancel) end |
#flush ⇒ void
This method returns an undefined value.
Execute the pending block immediately and cancel the timer.
137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 |
# File 'lib/philiprehberger/debounce/debouncer.rb', line 137 def flush args = nil should_execute = false @mutex.synchronize do if @pending args = @last_args should_execute = true @generation += 1 @pending = false @last_args = nil @called_leading = false @first_call_time = nil end end execute(args) if should_execute invoke_callback(@on_flush) end |
#last_result ⇒ Object?
Returns the result of the last block execution.
189 190 191 |
# File 'lib/philiprehberger/debounce/debouncer.rb', line 189 def last_result @mutex.synchronize { @last_result } end |
#metrics ⇒ Hash
Returns metrics about debouncer usage.
176 177 178 179 180 181 182 183 184 |
# File 'lib/philiprehberger/debounce/debouncer.rb', line 176 def metrics @mutex.synchronize do { call_count: @call_count, execution_count: @execution_count, suppressed_count: @call_count - @execution_count } end end |
#pending? ⇒ Boolean
Whether there is a pending execution.
160 161 162 |
# File 'lib/philiprehberger/debounce/debouncer.rb', line 160 def pending? @mutex.synchronize { @pending } end |
#pending_args ⇒ Array?
Returns the arguments that would be passed to the next execution.
167 168 169 170 171 |
# File 'lib/philiprehberger/debounce/debouncer.rb', line 167 def pending_args @mutex.synchronize do @pending ? @last_args : nil end end |
#reset_metrics ⇒ void
This method returns an undefined value.
Resets all metric counters to zero.
196 197 198 199 200 201 |
# File 'lib/philiprehberger/debounce/debouncer.rb', line 196 def reset_metrics @mutex.synchronize do @call_count = 0 @execution_count = 0 end end |