Skip to content

Instantly share code, notes, and snippets.

@igalshilman
Last active January 29, 2026 15:32
Show Gist options
  • Select an option

  • Save igalshilman/277de79c1d93727bb5cab1d2b186a870 to your computer and use it in GitHub Desktop.

Select an option

Save igalshilman/277de79c1d93727bb5cab1d2b186a870 to your computer and use it in GitHub Desktop.
import {
service,
serve,
type Context,
RestatePromise,
TerminalError,
} from "@restatedev/restate-sdk";
/***
* A durable async generator that persists its state across restarts.
*
*/
export class DurableAsyncGenerator<T, TReturn = any> {
/**
* Creates a new durable async generator from the provided async generator function.
*
* @param context restate context to persist side effects
* @param gen an async generator function
* @returns a durable version of the async generator
*/
public static create<T, TReturn = any>(
context: Context,
gen: () => AsyncGenerator<T, TReturn>
): RestatePromise<DurableAsyncGenerator<T, TReturn>> {
let generator: AsyncGenerator<T, TReturn> | undefined = undefined;
return context
.run("create", async () => {
generator = gen();
})
.map(() => {
return new DurableAsyncGenerator(context, generator);
});
}
private constructor(
private readonly context: Context,
private readonly generator: AsyncGenerator<T, TReturn> | undefined
) {}
next(): RestatePromise<IteratorResult<T, TReturn>> {
return this.context.run("next", async () => {
try {
if (!this.generator) {
throw new Error("Generator has been aborted");
}
return await this.generator.next();
} catch (error) {
const errorMessage =
error instanceof Error ? error.message : String(error);
throw new TerminalError(errorMessage);
}
});
}
[Symbol.asyncIterator]() {
return {
next: () => this.next(),
};
}
}
async function* randomGreeting() {
const greetings = [
"Hello",
"Hi",
"Greetings",
"Salutations",
"Howdy",
"Hey there",
"Good to see you",
"Welcome",
"Ahoy",
"Yo",
];
for (const greeting of greetings) {
yield greeting;
}
}
export const greeter = service({
name: "GeneratorService",
handlers: {
greet: async (ctx: Context, name: string) => {
const generator = await DurableAsyncGenerator.create(ctx, randomGreeting);
try {
for await (const greeting of generator) {
ctx.console.log(`${greeting}, ${name}!`);
}
} catch (error) {
ctx.console.error("Greeting generator failed:", error);
}
},
},
});
export type Greeter = typeof greeter;
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment