Class: Dommy::AbortSignal

Inherits:
Object
  • Object
show all
Includes:
EventTarget
Defined in:
lib/dommy/event.rb

Overview

‘AbortController` + `AbortSignal` subset. Signal fires an “abort” event and flips `[:aborted]` to true when the controller’s ‘abort()` is called; otherwise it stays inert.

Class Method Summary collapse

Instance Method Summary collapse

Methods included from EventTarget

#__deliver_event__, #add_event_listener, #dispatch_event, #invoke_listener, #remove_event_listener

Constructor Details

#initializeAbortSignal

Returns a new instance of AbortSignal.



867
868
869
870
# File 'lib/dommy/event.rb', line 867

def initialize
  @aborted = false
  @reason = nil
end

Class Method Details

.abort(reason = nil) ⇒ Object

Spec: ‘AbortSignal.abort(reason?)` returns a fresh, pre-aborted signal. Convenient for APIs that need an already-cancelled token.



824
825
826
827
828
# File 'lib/dommy/event.rb', line 824

def self.abort(reason = nil)
  signal = new
  signal.__mark_aborted__(reason)
  signal
end

.any(signals) ⇒ Object

Spec: ‘AbortSignal.any([sig, …])` returns a composite signal that aborts as soon as any of the inputs aborts. If any input is already aborted, the returned signal is pre-aborted with that input’s reason.



851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
# File 'lib/dommy/event.rb', line 851

def self.any(signals)
  composite = new
  list = Array(signals).select { |s| s.is_a?(AbortSignal) }
  already = list.find(&:aborted?)
  if already
    composite.__mark_aborted__(already.reason)
    return composite
  end

  list.each do |sig|
    sig.add_event_listener("abort", proc { composite.__mark_aborted__(sig.reason) })
  end

  composite
end

.timeout(ms, scheduler: nil) ⇒ Object

Spec: ‘AbortSignal.timeout(ms)` returns a signal that aborts itself after `ms` milliseconds with a `TimeoutError` reason. Without a Window/scheduler we fall back to a Thread-based timer so the signal works in vanilla CRuby; embedders that want microtask integration can pass a window via `schedule_via`.



835
836
837
838
839
840
841
842
843
844
845
# File 'lib/dommy/event.rb', line 835

def self.timeout(ms, scheduler: nil)
  signal = new
  reason = DOMException::TimeoutError.new("operation timed out")
  if scheduler
    scheduler.set_timeout(proc { signal.__mark_aborted__(reason) }, ms.to_i)
  else
    signal.__schedule_thread_timeout__(ms.to_i, reason)
  end

  signal
end

Instance Method Details

#__js_call__(method, args) ⇒ Object



915
916
917
918
919
920
921
922
923
924
925
926
# File 'lib/dommy/event.rb', line 915

def __js_call__(method, args)
  case method
  when "addEventListener"
    add_event_listener(args[0], args[1], args[2])
  when "removeEventListener"
    remove_event_listener(args[0], args[1])
  when "dispatchEvent"
    dispatch_event(args[0])
  when "throwIfAborted"
    throw_if_aborted
  end
end

#__js_get__(key) ⇒ Object



902
903
904
905
906
907
908
909
# File 'lib/dommy/event.rb', line 902

def __js_get__(key)
  case key
  when "aborted"
    @aborted
  when "reason"
    @reason
  end
end

#__js_set__(_key, _value) ⇒ Object



911
912
913
# File 'lib/dommy/event.rb', line 911

def __js_set__(_key, _value)
  nil
end

#__mark_aborted__(reason = nil) ⇒ Object



928
929
930
931
932
933
934
# File 'lib/dommy/event.rb', line 928

def __mark_aborted__(reason = nil)
  return if @aborted

  @aborted = true
  @reason = reason
  dispatch_event(Event.new("abort", "bubbles" => false, "cancelable" => false))
end

#__schedule_thread_timeout__(ms, reason) ⇒ Object

Background-thread timeout used by ‘AbortSignal.timeout` when no scheduler is provided. Kept package-private; tests can also drive the abort manually via `mark_aborted`.



875
876
877
878
879
880
881
882
# File 'lib/dommy/event.rb', line 875

def __schedule_thread_timeout__(ms, reason)
  Thread.new do
    sleep(ms.to_f / 1000.0)
    __mark_aborted__(reason)
  end

  nil
end

#aborted?Boolean

Returns:

  • (Boolean)


884
885
886
# File 'lib/dommy/event.rb', line 884

def aborted?
  @aborted
end

#reasonObject



888
889
890
# File 'lib/dommy/event.rb', line 888

def reason
  @reason
end

#throw_if_abortedObject Also known as: throwIfAborted

Spec: throws ‘signal.reason` if aborted, otherwise no-op. Used by consumer code that polls before doing async work.



894
895
896
897
898
# File 'lib/dommy/event.rb', line 894

def throw_if_aborted
  return unless @aborted

  raise @reason.is_a?(Exception) ? @reason : RuntimeError.new(@reason.to_s)
end