Skip to content

[Fizz] Finish abort in a scheduled task#36580

Merged
gnoff merged 1 commit into
facebook:mainfrom
gnoff:jstory/delayed-abort
Jun 1, 2026
Merged

[Fizz] Finish abort in a scheduled task#36580
gnoff merged 1 commit into
facebook:mainfrom
gnoff:jstory/delayed-abort

Conversation

@gnoff
Copy link
Copy Markdown
Collaborator

@gnoff gnoff commented May 30, 2026

Stacked on #36584

abort() currently performs both the synchronous transition into an aborted request and the reporting/completion of every unfinished task in the same call. This change splits those phases. Aborting now synchronously marks the request as aborted, captures the abort reason, claims pending tasks so already scheduled work cannot continue rendering them, and captures any DEV async debug information needed at the point of abort. Reporting and completing the claimed tasks is then performed from a scheduled finishAbort() callback.

This split does not yet allow a promise rejected by an abort listener to replace the abort reason: work remains blocked once the request has been aborted, and tests assert that abort-time rejections still report the original abort reason. It establishes the task boundary needed for a follow-up change to selectively process rejected suspended work before completing the remaining aborted tasks.

This is observable for streaming renders because abort cleanup may now happen after already available output is read. A Suspense boundary that was previously converted to client rendering before it could be serialized may instead be emitted as pending first and receive its client-render instruction when the scheduled abort completion runs.

The scheduled finish must also preserve abort-during-render behavior in renderers whose scheduler executes synchronously. The request tracks its currently executing task, and both abort phases leave that task alone so it can unwind through its normal abort path rather than being completed twice or reporting an internal control-flow value.

@meta-cla meta-cla Bot added the CLA Signed label May 30, 2026
@github-actions github-actions Bot added the React Core Team Opened by a member of the React Core Team label May 30, 2026
@react-sizebot
Copy link
Copy Markdown

react-sizebot commented May 30, 2026

Comparing: de8e005...686e01f

Critical size changes

Includes critical production bundles, as well as any change greater than 2%:

Name +/- Base Current +/- gzip Base gzip Current gzip
oss-stable/react-dom/cjs/react-dom.production.js = 6.84 kB 6.84 kB = 1.88 kB 1.88 kB
oss-stable/react-dom/cjs/react-dom-client.production.js = 614.26 kB 614.26 kB = 108.57 kB 108.57 kB
oss-experimental/react-dom/cjs/react-dom.production.js = 6.84 kB 6.84 kB = 1.88 kB 1.88 kB
oss-experimental/react-dom/cjs/react-dom-client.production.js = 680.19 kB 680.19 kB = 119.51 kB 119.51 kB
facebook-www/ReactDOM-prod.classic.js = 700.61 kB 700.61 kB = 123.09 kB 123.09 kB
facebook-www/ReactDOM-prod.modern.js = 690.93 kB 690.93 kB = 121.48 kB 121.48 kB

Significant size changes

Includes any change greater than 0.2%:

Expand to show
Name +/- Base Current +/- gzip Base gzip Current gzip
oss-stable-semver/react-server/cjs/react-server.development.js +0.76% 212.59 kB 214.21 kB +0.22% 37.52 kB 37.61 kB
oss-stable/react-server/cjs/react-server.development.js +0.76% 212.59 kB 214.21 kB +0.22% 37.52 kB 37.61 kB
oss-experimental/react-server/cjs/react-server.development.js +0.74% 217.38 kB 219.00 kB +0.22% 38.44 kB 38.53 kB
oss-stable-semver/react-server/cjs/react-server.production.js +0.53% 147.65 kB 148.43 kB +0.31% 25.98 kB 26.06 kB
oss-stable/react-server/cjs/react-server.production.js +0.53% 147.65 kB 148.43 kB +0.31% 25.98 kB 26.06 kB
oss-experimental/react-server/cjs/react-server.production.js +0.52% 151.19 kB 151.97 kB +0.28% 26.77 kB 26.84 kB
oss-experimental/react-markup/cjs/react-markup.development.js +0.38% 398.32 kB 399.84 kB +0.18% 72.25 kB 72.38 kB
oss-stable-semver/react-dom/cjs/react-dom-server-legacy.node.development.js +0.36% 411.33 kB 412.83 kB +0.18% 74.38 kB 74.51 kB
oss-stable-semver/react-dom/cjs/react-dom-server-legacy.browser.development.js +0.36% 411.33 kB 412.83 kB +0.18% 74.38 kB 74.51 kB
oss-stable/react-dom/cjs/react-dom-server-legacy.node.development.js +0.36% 411.36 kB 412.85 kB +0.18% 74.40 kB 74.54 kB
oss-stable/react-dom/cjs/react-dom-server-legacy.browser.development.js +0.36% 411.36 kB 412.86 kB +0.18% 74.40 kB 74.54 kB
oss-stable-semver/react-dom/cjs/react-dom-server.browser.development.js +0.36% 440.85 kB 442.45 kB +0.19% 78.55 kB 78.70 kB
oss-stable/react-dom/cjs/react-dom-server.browser.development.js +0.36% 440.93 kB 442.52 kB +0.19% 78.60 kB 78.75 kB
oss-stable-semver/react-dom/cjs/react-dom-server.edge.development.js +0.36% 441.63 kB 443.23 kB +0.19% 78.73 kB 78.88 kB
oss-stable/react-dom/cjs/react-dom-server.edge.development.js +0.36% 441.71 kB 443.31 kB +0.19% 78.78 kB 78.93 kB
facebook-www/ReactDOMServerStreaming-dev.modern.js +0.36% 426.20 kB 427.72 kB +0.15% 76.50 kB 76.61 kB
oss-stable-semver/react-dom/cjs/react-dom-server.node.development.js +0.36% 447.89 kB 449.49 kB +0.20% 78.53 kB 78.69 kB
oss-stable/react-dom/cjs/react-dom-server.node.development.js +0.36% 447.97 kB 449.56 kB +0.20% 78.58 kB 78.74 kB
facebook-www/ReactDOMServer-dev.modern.js +0.36% 421.87 kB 423.37 kB +0.19% 75.69 kB 75.83 kB
facebook-www/ReactDOMServer-dev.classic.js +0.35% 425.31 kB 426.81 kB +0.19% 76.30 kB 76.45 kB
oss-experimental/react-dom/cjs/react-dom-server-legacy.node.development.js +0.35% 426.65 kB 428.15 kB +0.19% 76.62 kB 76.76 kB
oss-experimental/react-dom/cjs/react-dom-server-legacy.browser.development.js +0.35% 426.65 kB 428.15 kB +0.19% 76.61 kB 76.76 kB
oss-experimental/react-dom/cjs/react-dom-server.browser.development.js +0.35% 457.21 kB 458.81 kB +0.20% 80.88 kB 81.04 kB
oss-experimental/react-dom/cjs/react-dom-server.edge.development.js +0.35% 458.22 kB 459.82 kB +0.19% 81.11 kB 81.27 kB
oss-experimental/react-dom/cjs/react-dom-server.node.development.js +0.34% 463.98 kB 465.57 kB +0.20% 80.89 kB 81.04 kB
oss-stable-semver/react-dom/cjs/react-dom-server.bun.development.js +0.31% 390.45 kB 391.65 kB +0.16% 74.02 kB 74.13 kB
oss-stable/react-dom/cjs/react-dom-server.bun.development.js +0.31% 390.53 kB 391.73 kB +0.16% 74.04 kB 74.16 kB
oss-experimental/react-dom/cjs/react-dom-server.bun.development.js +0.30% 404.78 kB 405.97 kB +0.11% 76.35 kB 76.44 kB
oss-stable-semver/react-dom/cjs/react-dom-server.browser.production.js +0.26% 279.13 kB 279.86 kB +0.22% 49.67 kB 49.78 kB
oss-stable/react-dom/cjs/react-dom-server.browser.production.js +0.26% 279.20 kB 279.93 kB +0.22% 49.70 kB 49.80 kB
oss-stable-semver/react-dom/cjs/react-dom-server-legacy.browser.production.js +0.26% 251.50 kB 252.15 kB +0.24% 45.30 kB 45.41 kB
oss-stable/react-dom/cjs/react-dom-server-legacy.browser.production.js +0.26% 251.52 kB 252.18 kB +0.24% 45.33 kB 45.44 kB
oss-experimental/react-markup/cjs/react-markup.production.js +0.25% 248.64 kB 249.27 kB +0.22% 46.09 kB 46.19 kB
facebook-www/ReactDOMServer-prod.modern.js +0.25% 258.07 kB 258.73 kB +0.24% 46.09 kB 46.20 kB
oss-stable-semver/react-dom/cjs/react-dom-server.bun.production.js +0.25% 280.17 kB 280.88 kB +0.18% 49.44 kB 49.53 kB
oss-stable/react-dom/cjs/react-dom-server.bun.production.js +0.25% 280.24 kB 280.95 kB +0.18% 49.46 kB 49.55 kB
facebook-www/ReactDOMServer-prod.classic.js +0.25% 260.41 kB 261.06 kB +0.25% 46.44 kB 46.55 kB
oss-experimental/react-dom/cjs/react-dom-server-legacy.browser.production.js +0.25% 261.34 kB 261.99 kB +0.25% 46.73 kB 46.85 kB
oss-experimental/react-dom/cjs/react-dom-server.browser.production.js +0.25% 290.60 kB 291.33 kB +0.22% 51.30 kB 51.41 kB
oss-stable-semver/react-dom/cjs/react-dom-server.edge.production.js +0.25% 285.03 kB 285.74 kB +0.19% 51.87 kB 51.97 kB
oss-stable/react-dom/cjs/react-dom-server.edge.production.js +0.25% 285.11 kB 285.82 kB +0.19% 51.90 kB 51.99 kB
oss-stable-semver/react-dom/cjs/react-dom-server-legacy.node.production.js +0.25% 256.70 kB 257.33 kB +0.22% 47.29 kB 47.39 kB
oss-stable/react-dom/cjs/react-dom-server-legacy.node.production.js +0.25% 256.73 kB 257.36 kB +0.22% 47.31 kB 47.42 kB
oss-experimental/react-dom/cjs/react-dom-server.bun.production.js +0.24% 291.48 kB 292.19 kB +0.19% 51.21 kB 51.31 kB
oss-stable-semver/react-dom/cjs/react-dom-server.node.production.js +0.24% 293.10 kB 293.81 kB +0.18% 51.72 kB 51.82 kB
oss-stable/react-dom/cjs/react-dom-server.node.production.js +0.24% 293.17 kB 293.88 kB +0.18% 51.75 kB 51.84 kB
oss-experimental/react-dom/cjs/react-dom-server.edge.production.js +0.24% 297.18 kB 297.89 kB +0.19% 53.78 kB 53.88 kB
oss-experimental/react-dom/cjs/react-dom-server-legacy.node.production.js +0.24% 267.12 kB 267.75 kB +0.21% 48.96 kB 49.06 kB
facebook-www/ReactDOMServerStreaming-prod.modern.js +0.24% 268.31 kB 268.95 kB +0.19% 49.08 kB 49.17 kB
oss-experimental/react-dom/cjs/react-dom-server.node.production.js +0.23% 304.90 kB 305.61 kB +0.19% 53.62 kB 53.72 kB
oss-experimental/react-markup/cjs/react-markup.react-server.development.js +0.22% 686.73 kB 688.26 kB +0.12% 121.83 kB 121.97 kB

Generated by 🚫 dangerJS against 686e01f

@gnoff gnoff force-pushed the jstory/delayed-abort branch from 36fb8bc to 1ffc433 Compare May 30, 2026 18:49
@gnoff gnoff requested review from eps1lon and unstubbable and removed request for unstubbable May 30, 2026 18:49
@gnoff gnoff force-pushed the jstory/delayed-abort branch 3 times, most recently from d4e3acb to c7e7131 Compare May 31, 2026 18:20
`abort()` currently performs both the synchronous transition into an aborted request and the reporting/completion of every unfinished task in the same call. This change splits those phases. Aborting now synchronously marks the request as aborted, captures the abort reason, claims pending tasks so already scheduled work cannot continue rendering them, and captures any DEV async debug information needed at the point of abort. Reporting and completing the claimed tasks is then performed from a scheduled `finishAbort()` callback.

This split does not yet allow a promise rejected by an abort listener to replace the abort reason: work remains blocked once the request has been aborted, and tests assert that abort-time rejections still report the original abort reason. It establishes the task boundary needed for a follow-up change to selectively process rejected suspended work before completing the remaining aborted tasks.

This is observable for streaming renders because abort cleanup may now happen after already available output is read. A Suspense boundary that was previously converted to client rendering before it could be serialized may instead be emitted as pending first and receive its client-render instruction when the scheduled abort completion runs.

The scheduled finish must also preserve abort-during-render behavior in renderers whose scheduler executes synchronously. The request tracks its currently executing task, and both abort phases leave that task alone so it can unwind through its normal abort path rather than being completed twice or reporting an internal control-flow value.
@gnoff gnoff force-pushed the jstory/delayed-abort branch from c7e7131 to 686e01f Compare May 31, 2026 21:01
@gnoff gnoff merged commit 3c882b4 into facebook:main Jun 1, 2026
237 checks passed
@gnoff gnoff deleted the jstory/delayed-abort branch June 1, 2026 06:33
github-actions Bot pushed a commit that referenced this pull request Jun 1, 2026
Stacked on #36584

`abort()` currently performs both the synchronous transition into an
aborted request and the reporting/completion of every unfinished task in
the same call. This change splits those phases. Aborting now
synchronously marks the request as aborted, captures the abort reason,
claims pending tasks so already scheduled work cannot continue rendering
them, and captures any DEV async debug information needed at the point
of abort. Reporting and completing the claimed tasks is then performed
from a scheduled `finishAbort()` callback.

This split does not yet allow a promise rejected by an abort listener to
replace the abort reason: work remains blocked once the request has been
aborted, and tests assert that abort-time rejections still report the
original abort reason. It establishes the task boundary needed for a
follow-up change to selectively process rejected suspended work before
completing the remaining aborted tasks.

This is observable for streaming renders because abort cleanup may now
happen after already available output is read. A Suspense boundary that
was previously converted to client rendering before it could be
serialized may instead be emitted as pending first and receive its
client-render instruction when the scheduled abort completion runs.

The scheduled finish must also preserve abort-during-render behavior in
renderers whose scheduler executes synchronously. The request tracks its
currently executing task, and both abort phases leave that task alone so
it can unwind through its normal abort path rather than being completed
twice or reporting an internal control-flow value.

DiffTrain build for [3c882b4](3c882b4)
github-actions Bot pushed a commit to code/lib-react that referenced this pull request Jun 1, 2026
Stacked on facebook#36584

`abort()` currently performs both the synchronous transition into an
aborted request and the reporting/completion of every unfinished task in
the same call. This change splits those phases. Aborting now
synchronously marks the request as aborted, captures the abort reason,
claims pending tasks so already scheduled work cannot continue rendering
them, and captures any DEV async debug information needed at the point
of abort. Reporting and completing the claimed tasks is then performed
from a scheduled `finishAbort()` callback.

This split does not yet allow a promise rejected by an abort listener to
replace the abort reason: work remains blocked once the request has been
aborted, and tests assert that abort-time rejections still report the
original abort reason. It establishes the task boundary needed for a
follow-up change to selectively process rejected suspended work before
completing the remaining aborted tasks.

This is observable for streaming renders because abort cleanup may now
happen after already available output is read. A Suspense boundary that
was previously converted to client rendering before it could be
serialized may instead be emitted as pending first and receive its
client-render instruction when the scheduled abort completion runs.

The scheduled finish must also preserve abort-during-render behavior in
renderers whose scheduler executes synchronously. The request tracks its
currently executing task, and both abort phases leave that task alone so
it can unwind through its normal abort path rather than being completed
twice or reporting an internal control-flow value.

DiffTrain build for [3c882b4](facebook@3c882b4)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

CLA Signed React Core Team Opened by a member of the React Core Team

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants