Class: Philiprehberger::Debounce::Debouncer

Inherits:
Object
  • Object
show all
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.

Examples:

debouncer = Philiprehberger::Debounce.debounce(wait: 0.3) { puts "saved" }
debouncer.call   # resets timer
debouncer.call   # resets timer again — block fires 0.3s after this call

Instance Method Summary collapse

Constructor Details

#initialize(wait:, leading: false, trailing: true, &block) ⇒ Debouncer

Returns a new instance of Debouncer.

Parameters:

  • wait (Float)

    delay in seconds

  • leading (Boolean) (defaults to: false)

    fire on the leading edge

  • trailing (Boolean) (defaults to: true)

    fire on the trailing edge

  • block (Proc)

    the block to execute

Raises:

  • (ArgumentError)


20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
# File 'lib/philiprehberger/debounce/debouncer.rb', line 20

def initialize(wait:, leading: false, trailing: true, &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

  @wait = wait
  @leading = leading
  @trailing = trailing
  @block = block
  @mutex = Mutex.new
  @pending = false
  @last_args = nil
  @called_leading = false
  @generation = 0
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).

Parameters:

  • args (Array)

    arguments forwarded to the block



43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
# File 'lib/philiprehberger/debounce/debouncer.rb', line 43

def call(*args)
  @mutex.synchronize do
    @last_args = args
    @pending = true
    @generation += 1
    current_gen = @generation

    # Leading edge: fire immediately on the first call of a new cycle
    if @leading && !@called_leading
      @called_leading = true
      execute(args)
    end

    # Start a new trailing timer
    if @trailing || @leading
      Thread.new do
        sleep @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
              execute(args_to_use)
            else
              @pending = false
              @called_leading = false
            end
          end
        end
      end
    end
  end
end

#cancelvoid

This method returns an undefined value.

Cancel any pending execution.



84
85
86
87
88
89
90
91
# File 'lib/philiprehberger/debounce/debouncer.rb', line 84

def cancel
  @mutex.synchronize do
    @generation += 1
    @pending = false
    @last_args = nil
    @called_leading = false
  end
end

#flushvoid

This method returns an undefined value.

Execute the pending block immediately and cancel the timer.



96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
# File 'lib/philiprehberger/debounce/debouncer.rb', line 96

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
    end
  end

  execute(args) if should_execute
end

#pending?Boolean

Whether there is a pending execution.

Returns:

  • (Boolean)


117
118
119
# File 'lib/philiprehberger/debounce/debouncer.rb', line 117

def pending?
  @mutex.synchronize { @pending }
end