Skip to main content

resonate-async-await-engine-typescript

The Resonate TypeScript SDK's async/await execution engine — import from @resonatehq/sdk/async, register async functions, eager ctx.run fan-out with Promise.all, Never-default retries and Exponential opt-in, and when to choose the async engine vs the generator engine. Introduced in v0.11.0. Use when writing new TypeScript workflows with async/await instead of function*/yield*.

الانتقال إلى التثبيت

معلومات المصدر

المستودع
resonatehq/resonate-skills
آخر نشاط في المصدر
٥ أغسطس ٢٠٢٦ في ٢٣:٢٩
لغة SKILL.md المكتشفة
الإنجليزية
النجوم
٦
التفرعات
٠

خيارات التثبيت

يُحدَّد Prompt الذي يراجع المصدر أولًا بشكل افتراضي. يمكنك التبديل إلى أمر مباشر أو تنزيل نسخة محلية.

مراجعة ملفات المصدر

اقرأ SKILL.md وأي ملفات مرافقة يعرضها SkillsMP قبل أن تقرر التثبيت.

عرض SKILL.md

SKILL.md
تعليمات المصدر · معاينة للقراءة فقط
name
resonate-async-await-engine-typescript
description
The Resonate TypeScript SDK's async/await execution engine — import from @resonatehq/sdk/async, register async functions, eager ctx.run fan-out with Promise.all, Never-default retries and Exponential opt-in, and when to choose the async engine vs the generator engine. Introduced in v0.11.0. Use when writing new TypeScript workflows with async/await instead of function*/yield*.
license
Apache-2.0
# Resonate Async/Await Engine (TypeScript) > **SDK version:** This skill reflects `@resonatehq/sdk` v0.11.4 (current on npm). The async/await engine was introduced in v0.11.0. ## Overview The TypeScript SDK ships two execution engines: | Engine | Import path | Function style | Release | |---|---|---|---| | **Generator engine** | `@resonatehq/sdk` | `function*` / `yield*` | v0.10.x and earlier; still current | | **Async/await engine** | `@resonatehq/sdk/async` | `async function` / `await` | v0.11.0+ | Both engines connect to the same Resonate Server, share the same durable-promise substrate, and support the same patterns (fan-out, human-in-the-loop, saga, etc.). The async engine is opt-in — existing generator-engine code is unaffected. For the generator engine, see `resonate-basic-ephemeral-world-usage-typescript` and `resonate-basic-durable-world-usage-typescript`. --- ## When to use the async engine - Writing new TypeScript workflows and prefer `async/await` over `function*`/`yield*` - Integrating Resonate into a codebase that already uses `async/await` throughout - Browser or edge environments: `@resonatehq/sdk/async` exports are browser-compatible ## When to keep the generator engine - Existing generator-engine code — there is no reason to migrate; the generator engine is fully supported - You need `ctx.detached()` with the full bounded-replay pattern (async engine also has `detach()` but check the SDK changelog for your version) --- ## Installation ```bash npm install @resonatehq/sdk@0.11.4 ``` Both engines are in the same package. The generator engine is the default export; the async engine is the `/async` sub-export. --- ## Basic usage ```typescript import { Resonate } from "@resonatehq/sdk/async"; const resonate = new Resonate({ url: "http://localhost:8001" }); // Register an async function resonate.register("greet", async (ctx, name: string) => { return `Hello, ${name}!`; }); // Start a workflow — resonate.run(id, funcOrName, ...args) → Promise<ResonateHandle<T>> const handle = await resonate.run("greet-001", "greet", "world"); // Await the result via the durable-promise subscription const result = await handle.result(); // result === "Hello, world!" ``` **Key differences from the generator engine:** - Import is `from "@resonatehq/sdk/async"` not `from "@resonatehq/sdk"` - Functions are `async function` not `function*` - `ctx.run(func, ...args)` in the async engine has **no id argument** (ID is auto-generated); in the generator engine `ctx.run(fn, ...args)` is also ID-less at the context level, but the API shape differs - No `beginRun` / `beginRpc` on the async-engine client — every `run` and `rpc` already returns a `ResonateHandle<T>` directly, so `beginRun` would be redundant --- ## Eager ctx.run and fan-out with Promise.all Inside an async workflow function, `ctx.run(func, ...args)` returns a `DurablePromise<T>` immediately — the child starts executing right away. Hold several and await them together for parallel fan-out: ```typescript resonate.register("processItems", async (ctx, items: string[]) => { // Start all children eagerly — they run in parallel const promises = items.map((item) => ctx.run(processOne, item) ); // Await all results const results = await Promise.all(promises); return results; }); resonate.register("processOne", async (ctx, item: string) => { // ... durable leaf work return `processed: ${item}`; }); ``` This is the async-engine equivalent of the generator engine's `ctx.beginRun` + sequential `yield*` pattern. --- ## Retries: Never default, Exponential opt-in The async/await engine defaults to **no retry** (`Never`) when no retry policy is specified. To opt into retries on a specific `ctx.run` call, pass `ctx.options({ retryPolicy: ... })`: ```typescript import { Resonate, Exponential, Never } from "@resonatehq/sdk/async"; resonate.register("reliableStep", async (ctx, taskId: string) => { // Default: Never retry — a failure propagates immediately const noRetryResult = await ctx.run(fragileOp, taskId); // Explicit Exponential retry on a specific child const retryResult = await ctx.run( flakeyOp, taskId, ctx.options({ retryPolicy: new Exponential() }), ); return { noRetryResult, retryResult }; }); ``` `Never`, `Exponential`, `Linear`, and `Constant` are all exported from `@resonatehq/sdk/async` and can be used with both engines. **Tip:** For saga-style compensation, let steps fail immediately with the default `Never` policy and catch in the outer function. --- ## ctx.options() in the async engine `ctx.options()` accepts the same fields as the generator engine: ```typescript ctx.options({ retryPolicy: new Exponential(), // retry policy (default: Never) target: "poll://any@workers", // worker group routing timeout: 30_000, // ms; defaults to 24h tags: { "env": "prod" }, // arbitrary tags on the child promise version: 1, // function version pin }) ``` --- ## Migration from generator engine | Generator engine | Async/await engine | |---|---| | `import { Resonate } from "@resonatehq/sdk"` | `import { Resonate } from "@resonatehq/sdk/async"` | | `function* workflow(ctx: Context, ...)` | `async function workflow(ctx: Context, ...)` | | `yield* ctx.run(fn, args)` | `await ctx.run(fn, args)` | | `yield* ctx.rpc(fn, args)` | `await ctx.rpc(fn, args)` | | `yield* ctx.sleep(ms)` | `await ctx.sleep(ms)` | | `const p = yield* ctx.promise(opts)` | `const dp = ctx.promise(opts)` — no await; returns `DurablePromise` with `.id` | | `const val = yield* p` | `const val = await dp` | | `resonate.beginRun(id, fn, ...args)` | `resonate.run(id, fn, ...args)` (returns handle) | | `await resonate.run(id, fn, ...args)` | `(await resonate.run(id, fn, ...args)).result()` | The server-side promise structure is identical; existing generator-engine workflows on the server are unaffected by the async engine. See the [SDK README migration guide](https://github.com/resonatehq/resonate-sdk-ts#migrating-from-generators) for the authoritative changelog. --- ## External promise resolution (human-in-the-loop) The async engine uses the same `resonate.promises.resolve()` API as the generator engine: ```typescript // Inside a workflow: park on an external decision resonate.register("awaitApproval", async (ctx, orderId: string) => { const dp = ctx.promise<{ approved: boolean }>(); // dp.id is available synchronously — surface it to whoever needs to approve console.log("Approval promise id:", dp.id); const decision = await dp; return decision; }); // In a webhook handler: resolve the parked promise const data = Buffer.from(JSON.stringify({ approved: true })).toString("base64"); await resonate.promises.resolve(promiseId, { data }); ``` --- ## Full runnable example ```typescript import { Resonate, Exponential } from "@resonatehq/sdk/async"; const resonate = new Resonate({ url: "http://localhost:8001" }); resonate.register("summarize", async (ctx, texts: string[]) => { // Fan-out: summarize each text in parallel const summaries = await Promise.all( texts.map((t) => ctx.run( summarizeOne, t, ctx.options({ retryPolicy: new Exponential() }), ) ) ); return summaries.join("\n"); }); resonate.register("summarizeOne", async (ctx, text: string) => { // ... call an LLM or summarization service return `Summary of: ${text.slice(0, 50)}`; }); const handle = await resonate.run("summarize-job-001", "summarize", [ "First document ...", "Second document ...", ]); console.log(await handle.result()); await resonate.stop(); ```
عرض على GitHub