Skip to content

Pin async iterator interleavings that follow WebIDL - #7475

Merged
jasnell merged 1 commit into
mainfrom
jasnell/ts-streams-async-iterator-order
Sep 24, 2026
Merged

jasnell merged 1 commit into
mainfrom
jasnell/ts-streams-async-iterator-order

Conversation

@jasnell

@jasnell jasnell commented Sep 22, 2026

Copy link
Copy Markdown
Collaborator

WebIDL clears an async iterator's ongoing promise whenever a next() settles. A next() from a continuation registered on an earlier next() therefore reads ahead of calls already queued, including a queued return(), and a next() after a rejected one reports done. TS follows the spec, as WPT async-iterator.any checks; C++ serializes every call. Readable ledger #22.

@jasnell
jasnell requested review from guybedford and npaun September 22, 2026 19:59
@jasnell
jasnell requested review from a team as code owners September 22, 2026 19:59
@ask-bonk

ask-bonk Bot commented Sep 22, 2026

Copy link
Copy Markdown
Contributor

LGTM!

github run

@guybedford guybedford left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Traced all three shapes through the WebIDL next()/return() algorithm: fulfillSteps/rejectSteps null the ongoing promise before the outer next() settles, so a continuation registered on it runs nextSteps synchronously ahead of the chained calls, which gives exactly the TS pins (n3 'b'/n2 'c'; n2 'b' ahead of a queued return() that still cancels; done after a rejected next()). readable.ts's state.current + read-request clearing mirrors that structure. Ran //src/tests/streams/readable/... with --nocache_test_results (15/15) and confirmed the new test fails on both sides when the pin is flipped.

Two optional nits inline. One out-of-scope aside, unverified: in next() (readable.ts:564), when current is undefined and the read is served synchronously from the queue, chunk() clears current and the assignment then overwrites it with the already-resolved promise, so the following call chains a microtask later where WebIDL (ongoing = null) would run it synchronously. Not exercised here; noting in case it's worth a later pin.

Comment thread src/tests/streams/readable/async-iteration.js
Comment thread src/tests/streams/readable/AGENTS.md Outdated
@jasnell
jasnell force-pushed the jasnell/ts-streams-async-iterator-order branch from 7ebfaa2 to 2e87599 Compare September 23, 2026 22:44
WebIDL clears an async iterator's ongoing promise whenever a next()
settles. A next() from a continuation registered on an earlier next()
therefore reads ahead of calls already queued, including a queued
return(), and a next() after a rejected one reports done. TS follows
the spec, as WPT async-iterator.any checks; C++ serializes every call.
Readable ledger #22.
@jasnell
jasnell force-pushed the jasnell/ts-streams-async-iterator-order branch from 2e87599 to 3dbc959 Compare September 23, 2026 23:21
@jasnell
jasnell merged commit cb14d55 into main Sep 24, 2026
22 checks passed
@jasnell
jasnell deleted the jasnell/ts-streams-async-iterator-order branch September 24, 2026 00:54
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants