Skip to content

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

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

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

Conversation

@rubennorte

@rubennorte rubennorte commented Jun 30, 2026

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

Reviewed By: javache

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.

…eact#57381)

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

Reviewed By: javache

Differential Revision: D110185361
@meta-codesync meta-codesync Bot changed the title Make AbortSignal.any spec-compliant with a multi-pass implementation Make AbortSignal.any spec-compliant with a multi-pass implementation (#57381) Jul 1, 2026
@rubennorte rubennorte force-pushed the export-D110185361 branch from 97ac336 to a20bd80 Compare July 1, 2026 10:04
@meta-codesync meta-codesync Bot closed this in 1136e41 Jul 1, 2026
@meta-codesync meta-codesync Bot added the Merged This PR has been merged. label Jul 1, 2026
@meta-codesync

meta-codesync Bot commented Jul 1, 2026

Copy link
Copy Markdown

This pull request has been merged in 1136e41.

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. Merged This PR has been merged. meta-exported p: Facebook Partner: Facebook Partner

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant