Skip to content

Commit 850f609

Browse files
committed
readme
1 parent d7161a6 commit 850f609

File tree

1 file changed

+84
-18
lines changed

1 file changed

+84
-18
lines changed

README.md

Lines changed: 84 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -415,61 +415,127 @@ export const exampleWorkflow = workflow.define({
415415
});
416416
```
417417

418-
### Awaiting events with `ctx.awaitEvent`
418+
### Waiting for external events
419419

420420
Use `ctx.awaitEvent` inside a workflow handler to pause until an external event
421-
is delivered. This is useful for human-in-the-loop flows or coordinating with
422-
other systems.
421+
is triggered. This is useful for human-in-the-loop flows or coordinating with
422+
other asynchronous flows. Wait for an indefinite amount of time and continue
423+
when the event is triggered.
423424

424425
At its simplest, you can wait for an event **by name**:
425426

426427
```ts
427-
await ctx.awaitEvent({ name: "approval" });
428+
await ctx.awaitEvent({ name: "eventName" });
428429
```
429430

430-
or for a **specific event by ID**:
431+
This will wait for the first un-consumed event with the name "eventName", and
432+
will continue immediately if one was already sent. Events are sent by calling
433+
`workflow.sendEvent` from a mutation or action:
431434

432435
```ts
433-
await ctx.awaitEvent({ id: signalId });
436+
await workflow.sendEvent(ctx, {
437+
name: "eventName",
438+
workflowId,
439+
});
434440
```
435441

436-
To unblock a waiting workflow, call `workflow.sendEvent` from a mutation or
437-
action. You can send a value with the event, or send an error that will cause
438-
`ctx.awaitEvent` to throw. See
439-
[`example/convex/passingSignals.ts`](./example/convex/passingSignals.ts) for a
440-
complete example of creating events, passing their IDs around, and sending
441-
signals.
442+
Note: You must send the event on the same workflow component that is waiting for
443+
it, and the workflowId must match the ID of the workflow that is waiting for it.
444+
445+
#### Sending values or errors with the event
442446

443-
#### Sharing event definitions with `defineEvent`
447+
You can send a value with the event using the `value` property. For type safety
448+
and runtime validation, provide a validator on the sending and receiving sides.
449+
450+
```ts
451+
const sharedValidator = v.number();
452+
453+
// In the workflow:
454+
const event = await ctx.awaitEvent({ name, validator: sharedValidator });
455+
456+
// From elsewhere:
457+
await workflow.sendEvent(ctx, { name, workflowId, value: 42 });
458+
```
459+
460+
To send an error, use the `error` property. This will cause `ctx.awaitEvent` to
461+
throw an error.
462+
463+
```ts
464+
await workflow.sendEvent(ctx, { name, workflowId, error: "An error occurred" });
465+
```
466+
467+
#### Sharing event definitions
444468

445469
Use `defineEvent` to define an event's name and validator in one place, then
446470
share it between the workflow and the sender:
447471

448472
```ts
449473
const approvalEvent = defineEvent({
450-
name: "approval" as const,
474+
name: "approval",
451475
validator: v.object({ approved: v.boolean() }),
452476
});
453477

454478
// In the workflow:
455479
const approval = await ctx.awaitEvent(approvalEvent);
456480

457481
// From a mutation:
458-
await workflow.sendEvent(ctx, { ...approvalEvent, workflowId, value: { approved: true } });
482+
const value = { approved: true };
483+
await workflow.sendEvent(ctx, { ...approvalEvent, workflowId, value });
459484
```
460485

461486
See [`example/convex/userConfirmation.ts`](./example/convex/userConfirmation.ts)
462487
for a full approval flow built this way.
463488

489+
Note: this is just a convenience to create a typed { event, validator } pair.
490+
491+
#### Waiting for dynamically created events by ID
492+
493+
You can also dynamically create an event with `createEvent`:
494+
495+
```ts
496+
const eventId = await workflow.createEvent(ctx, {
497+
name: "userResponse",
498+
workflowId,
499+
});
500+
```
501+
502+
Then wait for it by ID in the workflow:
503+
504+
```ts
505+
await ctx.awaitEvent({ id: eventId });
506+
```
507+
508+
This works well when there are dynamically defined events, for instance a tool
509+
that is waiting for a response from a user. You would save the eventId somewhere
510+
to be able to send the event later with `workflow.sendEvent`:
511+
512+
```ts
513+
await workflow.sendEvent(ctx, { id: eventId });
514+
```
515+
516+
Similar to named events, you can also send a value or error with the event.
517+
518+
See [`example/convex/passingSignals.ts`](./example/convex/passingSignals.ts) for
519+
a complete example of creating events, passing their IDs around, and sending
520+
signals.
521+
464522
### Running nested workflows with `ctx.runWorkflow`
465523

466524
Use `ctx.runWorkflow` to run another workflow as a single step in the current
467525
one. The parent workflow waits for the nested workflow to finish and receives
468-
its return value: `const result = await ctx.runWorkflow(internal.example.childWorkflow, { args });`
526+
its return value:
527+
`const result = await ctx.runWorkflow(internal.example.childWorkflow, { args });`
469528

470529
You can also specify scheduling options like `{ runAfter: 5000 }` to delay the
471-
nested workflow. See [`example/convex/nestedWorkflow.ts`](./example/convex/nestedWorkflow.ts)
472-
for a complete parent/child workflow example.
530+
nested workflow. See
531+
[`example/convex/nestedWorkflow.ts`](./example/convex/nestedWorkflow.ts) for a
532+
complete parent/child workflow example.
533+
534+
To associate the child workflow with the parent in your own tables, you can pass
535+
the `ctx.workflowId` to the child workflow as an argument, and/or return the
536+
child's workflowId to the parent.
537+
538+
The status of the parent workflow will include any active child workflowIds.
473539

474540
## Tips and troubleshooting
475541

0 commit comments

Comments
 (0)