AbortSignal-like abstraction without event dispatch?

While reading the discussion in whatwg/dom#1461, I started wondering whether the underlying abstraction behind AbortSignal could be separated from its event-based aspects.

Related discussion:

  • whatwg/dom#1461 β€” "AbortSignal.timeout() may not abort past the timeout"

Motivation

AbortSignal today is fundamentally event-loop driven. In practice, this means that cancellation is not observable while the current agent is executing long-running synchronous work.

However, many use cases seem to only require synchronous observation of cancellation:

while (!signal.aborted) {
  performWork();
}

In such cases, what matters is not event delivery, but the ability to observe cancellation state (aborted, reason) and react to it (throwIfAborted()).

Observation

AbortSignal currently combines multiple concerns:

  1. Cancellation state

    • aborted
    • reason
  2. Cancellation observation

    • throwIfAborted()
  3. Notification mechanism

    • "abort" event via EventTarget

The issue above suggests that (1) and (2) are sometimes needed without (3), especially when event-loop progress is not guaranteed.

Question

Would it make sense to think about an "AbortSignal-like" abstraction that includes:

  • aborted
  • reason
  • throwIfAborted()
  • (possibly) composition helpers like any()

but does not require event dispatch?

In other words:

AbortSignal
  = AbortSignal-like (state + logic)
  + EventTarget (notification)

Possible implications

If such an abstraction existed, different implementations could be possible:

  • DOM AbortSignal (event-driven)
  • Timeout-based signals (clock-driven)
  • Shared-state signals (e.g. across agents, possibly via shared memory)
  • Userland implementations

In particular, some environments may want cancellation state to be observable independently of the event loop, while still optionally supporting event-based notifications.

Scope

I am not proposing changes to AbortSignal itself.

Rather, I am wondering:

  • Is this separation of concerns meaningful?
  • Would an "AbortSignal-like" abstraction make sense at the language level?
  • Or is this better left as platform-specific or userland patterns?

Curious to hear thoughts, especially if similar ideas have been explored before.

Related discussion about one possible implementation approach:

I have thought about this a great deal and written up most of it at GitHub - bakkot/structured-concurrency-for-js: Exploring adding structured concurrency to JavaScript Β· GitHub. See in particular this issue.

Short version: I don't think it's feasible to have this be a separate mechanism from AbortSignal, and I do think we need some way to actively respond to cancelation, but that doesn't mean we have to keep using the event machinery; indeed native consumers already bypass the event machinery when using AbortSignal. And I think this could be a general interface where you could implement your own AbortSignal-like thing; consumers in the language (although unfortunately possibly not the DOM) would consume it via a well-defined public interface which could be implemented by anything.

1 Like