Description
Currently it is not possible to subclass these, despite DOM interfaces doing so (TaskController
/TaskSignal
). Currently each of the properties in AbortSignal
asserts that it is either an AbortSignal
or TaskSignal
, and if it is not it will throw. This means doing the following results in an error:
class WatchSignal extends AbortSignal {}
new WatchSignal() // This throws... but we can try Object.create()
const ws = Object.create(WatchSignal.prototype) // We got signal!
console.assert( ws instanceof AbortSignal )
ws.aborted // throws with invocation or "does not implement interface AbortSignal" error
Instead one has to use a delegate class which means re-implementing all methods and Symbol.hasInstance
if you want to keep brand checks.
Making AbortController
extensible comes with its own problems, however. Chiefly how does one associate the controller with the signal?
Why make it extensible?
Of course the request to make it extensible begs the question as to why. I think TaskController
/TaskSignal
provides good precedent here - extended versions are useful to pack extra data into an interface, especially one that can be passed between contexts. TaskSignals
extend the concept to add a priority
and prioritychange
event, and here are some other ideas that could be implemented in userland:
PauseController
/PauseSignal
with apaused
property andpausedchanged
event, allowing for pausing/resuming of tasks.RetryController
/RetrySignal
, with aretry
number property, allowing consumers to know how often to retry a task.DelayController
/DelaySignal
, which has awaitTime
number property, allowing consumers to delay a task before execution.
All of these subclasses can still be passed freely to downstream functions like fetch
, but avoid the need to send a whole slew of signals to a function.