Class: Philiprehberger::Debounce::Throttler

Inherits:
Object
  • Object
show all
Defined in:
lib/philiprehberger/debounce/throttler.rb

Overview

Limits execution to at most once per interval.

Unlike Debouncer, which delays until calls stop, Throttler guarantees a maximum execution frequency regardless of how often #call is invoked.

Examples:

throttler = Philiprehberger::Debounce.throttle(interval: 1.0) { |x| puts x }
10.times { throttler.call("hi") }  # executes at most once per second

Instance Method Summary collapse

Constructor Details

#initialize(interval:, leading: true, trailing: false, &block) ⇒ Throttler

Returns a new instance of Throttler.

Parameters:

  • interval (Float)

    minimum time between executions in seconds

  • leading (Boolean) (defaults to: true)

    fire on the leading edge

  • trailing (Boolean) (defaults to: false)

    fire on the trailing edge

  • block (Proc)

    the block to execute

Raises:

  • (ArgumentError)


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

def initialize(interval:, leading: true, trailing: false, &block)
  raise ArgumentError, 'block is required' unless block
  raise ArgumentError, 'interval must be positive' unless interval.positive?
  raise ArgumentError, 'at least one of leading or trailing must be true' if !leading && !trailing

  @interval = interval
  @leading = leading
  @trailing = trailing
  @block = block
  @mutex = Mutex.new
  @condition = ConditionVariable.new
  @last_args = nil
  @pending = false
  @trailing_scheduled = false
  @last_execution_time = nil
end

Instance Method Details

#call(*args) ⇒ void

This method returns an undefined value.

Invoke the throttler with optional arguments.

If enough time has elapsed since the last execution, the block runs immediately (leading edge). Otherwise the arguments are stored and the block fires at the end of the current interval (trailing 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
# File 'lib/philiprehberger/debounce/throttler.rb', line 43

def call(*args)
  @mutex.synchronize do
    now = monotonic_now
    @last_args = args
    @pending = true

    if @last_execution_time.nil? || (now - @last_execution_time) >= @interval
      # Enough time has passed — fire immediately if leading
      if @leading
        @last_execution_time = now
        @pending = false
        execute(args)
      elsif @trailing
        schedule_trailing
      end
    elsif @trailing
      schedule_trailing
    end
  end
end

#cancelvoid

This method returns an undefined value.

Cancel any pending trailing execution.



67
68
69
70
71
72
73
# File 'lib/philiprehberger/debounce/throttler.rb', line 67

def cancel
  @mutex.synchronize do
    @pending = false
    @last_args = nil
    @condition.signal
  end
end

#flushvoid

This method returns an undefined value.

Execute the pending block immediately and cancel the trailing timer.



78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
# File 'lib/philiprehberger/debounce/throttler.rb', line 78

def flush
  args = nil
  should_execute = false

  @mutex.synchronize do
    if @pending
      args = @last_args
      should_execute = true
      @pending = false
      @last_args = nil
      @last_execution_time = monotonic_now
      @condition.signal
    end
  end

  execute(args) if should_execute
end

#pending?Boolean

Whether there is a pending trailing execution.

Returns:

  • (Boolean)


99
100
101
# File 'lib/philiprehberger/debounce/throttler.rb', line 99

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