Skip to content

Add defer functionality to generator executor - #64157

Open
Wesley Wigham (weswigham) wants to merge 1 commit into
microsoft:mainfrom
weswigham:generator-defer
Open

Add defer functionality to generator executor#64157
Wesley Wigham (weswigham) wants to merge 1 commit into
microsoft:mainfrom
weswigham:generator-defer

Conversation

@weswigham

Copy link
Copy Markdown
Member

Add a defer function and support to the api.batch executor for it, allowing you to queue work to run within the current batching context without blocking execution of the current active generator - essentially the generator equivalent of an unawaited async function call in the async API.

cc Titian Cernicova-Dragomir (@dragomirtitian) who specifically requested built-in support for this, since it's cumbersome to wire up without owning the generator executor, but when you own it, it's comparably not bad to add.

Copilot AI 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.

🟡 Changes recommended

Deferred failures can prematurely interrupt foreground work, and completed generators cause quadratic scanning.

Once you've addressed the issues Copilot identified, you can request another Copilot review.

Pull request overview

Adds deferred generator work to synchronous API batching without returning deferred results.

Changes:

  • Adds defer and deferred-work scheduling.
  • Refactors request execution, deduplication, and result mapping.
  • Adds runtime coverage for deferred execution and errors.
File summaries
File Description
packages/typescript/src/api/sync/generatorSupport.ts Implements deferred request execution.
packages/typescript/src/api/sync/api.ts Exposes defer through synchronous batching.
packages/typescript/src/api/async/api.ts Updates sync-generation directives.
packages/typescript/test/sync/api-generators.test.ts Tests batching and deferred behavior.
Review details
  • Files reviewed: 4/4 changed files
  • Comments generated: 2
  • Review effort level: Balanced

💡 Add a code-review agent skill for context-aware, tailored reviews. Learn more in the docs.

return responseIndex;
};
// TODO: Use Iterator.prototype.filter when target >= ES2025
const roundGenerators = [...registeredGenerators].filter(generator => requestsByGenerator.has(generator));

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

It does, but... nah. registeredGenerators is ever-growing when deferrals can add new generators to the list each turn, so this is just required - we could track only unfinished ones and finished ones seperately, yes but then we're potentially allocating two new arrays per turn instead of allocating one and rarely growing one. Like the comment says, best would be iterator filter in the future to skip the filtered collection alloc entirely. 🤷‍♂️

Weak set for duplicate detection is like.... sure? But also no? Technically correct but accomplishes very little in practice - GC pressure of generator objects shouldn't be a real concern for any real code.

const responseIndex = responseIndices.get(generator)!;
if (typeof responseIndex === "number") {
const result = responses[responseIndex];
advanceGenerator(generator, result.result, result.error || undefined);
export { all, defer } from "./generatorSupport.ts";
import {
all,
type APIRequestGenerator,

@dragomirtitian Titian Cernicova-Dragomir (dragomirtitian) Sep 4, 2026

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.

Can we expose UndeferredAPIRequestGenerator and APIRequestGenerator. If the generators are recursive they need a type. We could redefine some of these types on the usage side but they are now more complicated that a simple generator.

If we expose it, I'm not sure UndeferredAPIRequestGenerator is the best name for the common case of the generator, and this is the type that is needed as the return type since APIRequestGenerator is a union and does not work well as a generator return type:

function *testNotOk(n: number): sync.APIRequestGenerator<number> {
    if(n === 0) return 0;
    return yield * testNotOk(n-1)
}


function *testOk(n: number): sync.UndeferredAPIRequestGenerator<number> {
    if(n === 0) return 0;
    return yield * testOk(n-1)
}

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

Labels

Author: Team For Uncommitted Bug PR for untriaged, rejected, closed or missing bug

Projects

Status: Not started

Development

Successfully merging this pull request may close these issues.

3 participants