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.



497
498
499
500
# File 'lib/dommy/event.rb', line 497

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.



454
455
456
457
458
# File 'lib/dommy/event.rb', line 454

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.



481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
# File 'lib/dommy/event.rb', line 481

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`.



465
466
467
468
469
470
471
472
473
474
475
# File 'lib/dommy/event.rb', line 465

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



545
546
547
548
549
550
551
552
553
554
555
556
# File 'lib/dommy/event.rb', line 545

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



532
533
534
535
536
537
538
539
# File 'lib/dommy/event.rb', line 532

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

#__js_set__(_key, _value) ⇒ Object



541
542
543
# File 'lib/dommy/event.rb', line 541

def __js_set__(_key, _value)
  nil
end

#__mark_aborted__(reason = nil) ⇒ Object



558
559
560
561
562
563
564
# File 'lib/dommy/event.rb', line 558

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`.



505
506
507
508
509
510
511
512
# File 'lib/dommy/event.rb', line 505

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)


514
515
516
# File 'lib/dommy/event.rb', line 514

def aborted?
  @aborted
end

#reasonObject



518
519
520
# File 'lib/dommy/event.rb', line 518

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.



524
525
526
527
528
# File 'lib/dommy/event.rb', line 524

def throw_if_aborted
  return unless @aborted

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