Skip to content

Make AbortSignal.any spec-compliant with a multi-pass implementation#57381

Open
rubennorte wants to merge 1 commit into
react:mainfrom
rubennorte:export-D110185361
Open

Make AbortSignal.any spec-compliant with a multi-pass implementation#57381
rubennorte wants to merge 1 commit into
react:mainfrom
rubennorte:export-D110185361

Conversation

@rubennorte

Copy link
Copy Markdown
Contributor

Summary:
AbortSignal.any previously processed the input signals in a single pass, attaching an abort listener to each signal as it iterated and unwinding them with a cleanup() call whenever it later encountered an already-aborted (or invalid) signal. This deviates from the specification, which validates the whole list, then scans it for an already-aborted signal (returning an aborted signal immediately), and only then subscribes to the remaining signals.

Process the list in three passes instead:

  • The first pass validates that every entry is an AbortSignal, throwing before any other work so an invalid entry never short-circuits ahead of an earlier already-aborted one.
  • The second pass returns an already-aborted signal (via AbortSignal.abort(reason)) if any input is already aborted. No listeners are registered yet, so there is nothing to unwind on an early return.
  • The third pass subscribes to every signal, since all of them are valid and none is aborted at that point.

This removes the need to clean up partially-registered listeners mid-setup and matches the behavior described in the DOM specification.

Also remove a dead branch in AbortController's getSignal helper. The controller === null ? 'null' : typeof controller expression was unreachable for null input because the preceding controller[SIGNAL_KEY] access already throws a TypeError on null/undefined, and it only existed by suppressing a Flow invalid-compare error. Use typeof controller directly so the suppression is no longer needed.

Changelog:
[General][Fixed] - Make AbortSignal.any() process its input signals in multiple passes to match the DOM specification

Differential Revision: D110185361

Summary:
`AbortSignal.any` previously processed the input signals in a single pass, attaching an abort listener to each signal as it iterated and unwinding them with a `cleanup()` call whenever it later encountered an already-aborted (or invalid) signal. This deviates from the specification, which validates the whole list, then scans it for an already-aborted signal (returning an aborted signal immediately), and only then subscribes to the remaining signals.

Process the list in three passes instead:

- The first pass validates that every entry is an `AbortSignal`, throwing before any other work so an invalid entry never short-circuits ahead of an earlier already-aborted one.
- The second pass returns an already-aborted signal (via `AbortSignal.abort(reason)`) if any input is already aborted. No listeners are registered yet, so there is nothing to unwind on an early return.
- The third pass subscribes to every signal, since all of them are valid and none is aborted at that point.

This removes the need to clean up partially-registered listeners mid-setup and matches the behavior described in the DOM specification.

Also remove a dead branch in `AbortController`'s `getSignal` helper. The `controller === null ? 'null' : typeof controller` expression was unreachable for `null` input because the preceding `controller[SIGNAL_KEY]` access already throws a `TypeError` on `null`/`undefined`, and it only existed by suppressing a Flow `invalid-compare` error. Use `typeof controller` directly so the suppression is no longer needed.

Changelog:
[General][Fixed] - Make `AbortSignal.any()` process its input signals in multiple passes to match the DOM specification

Differential Revision: D110185361
@meta-cla meta-cla Bot added the CLA Signed This label is managed by the Facebook bot. Authors need to sign the CLA before a PR can be reviewed. label Jun 30, 2026
@meta-codesync

meta-codesync Bot commented Jun 30, 2026

Copy link
Copy Markdown

@rubennorte has exported this pull request. If you are a Meta employee, you can view the originating Diff in D110185361.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

CLA Signed This label is managed by the Facebook bot. Authors need to sign the CLA before a PR can be reviewed. meta-exported p: Facebook Partner: Facebook Partner

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant