Last active
January 29, 2026 15:32
-
-
Save igalshilman/277de79c1d93727bb5cab1d2b186a870 to your computer and use it in GitHub Desktop.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| 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