Sharing promises between scopes
Operations like Timers and Activities are cancelled by the cancellation scope they were created in. However, promises returned by these operations can be awaited in different scopes. Example showing activities started in the root scope:
```ts
export async function sharedScopes(): Promise<any> {
// Start activities in the root scope
const p1 = httpGetJSON('http://url1.ninja');
const p2 = httpGetJSON('http://url2.ninja');
const scopePromise = CancellationScope.cancellable(async () => {
const first = await Promise.race([p1, p2]);
// Does not cancel activity1 or activity2 as they're linked to the root scope
CancellationScope.current().cancel();
return first;
});
return await scopePromise;
// The Activity that did not complete will effectively be cancelled when
// Workflow completes unless the Activity is awaited:
// await Promise.all([p1, p2]);
}
```
Shield activity from cancellation by starting in nonCancellable scope
Example showing how to shield an Activity from cancellation by starting it in a nonCancellable scope:
```ts
export async function shieldAwaitedInRootScope(): Promise<any> {
let p: Promise<any> | undefined = undefined;
await CancellationScope.nonCancellable(async () => {
p = httpGetJSON('http://example.com'); // <-- Start activity in nonCancellable scope without awaiting completion
});
// Activity is shielded from cancellation even though it is awaited in the cancellable root scope
return p;
}
```
Continue-As-New TypeScript example with state parameter
Example of calling continueAsNew() in TypeScript:
```typescript
return await wf.continueAsNew<typeof clusterManagerWorkflow>({
state: manager.getState(),
testContinueAsNew: input.testContinueAsNew
});
```
This example shows how to continue as new by passing the current state from the manager and forwarding the test hook parameter to the new Workflow execution.
Continue-As-New creates new Workflow Execution with same Workflow Id
Continue-As-New lets a Workflow Execution close successfully and creates a new Workflow Execution. The new Workflow Execution keeps the same Workflow Id but gets a new Run Id and a fresh Event History. It is in the same chain as the original Workflow.
Continue-As-New passes current state as parameter to new execution
When using Continue-As-New, design your Workflow parameters so that you can pass in the 'current state' when you Continue-As-New into the next Workflow run. This state is typically set to None for the original caller of the Workflow.
Use Continue-As-New when approaching Event History Limits
Use Continue-As-New when your Workflow might hit Event History Limits. This is useful as a checkpoint when your Workflow gets too long or approaches certain scaling limits.
Workflow parameter structure for Continue-As-New
Design your Workflow input interface to include an optional state parameter. Example:
```typescript
export interface ClusterManagerInput {
state?: ClusterManagerState;
}
export async function clusterManagerWorkflow(input: ClusterManagerInput = {}): Promise<ClusterManagerStateSummary> {
// workflow implementation
}
```
The state parameter allows passing the current state to the new Workflow execution when continuing as new.
Use continueAsNew() function to continue as new in TypeScript
Call the continueAsNew() function inside your Workflow with the same type as your Workflow. This stops the Workflow right away and starts a new one. Pass the current state and any other necessary parameters to the new Workflow execution.
TypeScript SDK workflow documentation topics
The TypeScript SDK provides documentation covering the following workflow-related topics: Workflow basics, Child Workflows, Continue-As-New, Message passing, Cancellation, Cancellation scopes, Timeouts, Schedules, Timers, Versioning, and Workflow Streams.
List Schedules with client.schedule.list()
To list all available Schedules, use client.schedule.list() which returns an async iterable. Iterate over it with for await to collect all Schedule objects and their respective Schedule IDs.
Pause a Schedule with handle.pause()
To pause a Schedule, use client.schedule.getHandle(scheduleId) and call handle.pause(). Pausing a Schedule temporarily stops all future Workflow Runs associated with the Schedule. This is useful for temporarily halting Workflows during maintenance.
Trigger a Schedule with handle.trigger()
To trigger an immediate action with a given Schedule, use client.schedule.getHandle(scheduleId) and call handle.trigger(). By default, this action is subject to the Schedule's Overlap Policy. This executes the Workflow outside of its scheduled time.
Update a Schedule with handle.update()
To update an existing Schedule, use client.schedule.getHandle(scheduleId) and call handle.update() with a callback function. The callback receives a ScheduleDescription object and should return a ScheduleUpdateOptions object with the modified configuration. This allows changing the Schedule's configuration such as arguments, timing, or other properties.
Schedule Overlap Policy affects paused Workflows
If a Workflow Execution started by a Schedule is paused, it remains open and counts as the running execution for overlap policy decisions. Schedule behavior is governed by the Schedule's Overlap Policy.
Temporal Cron Jobs with cronSchedule option
A Temporal Cron Job is a series of Workflow Executions that occur when a Cron Schedule is provided. Set the cronSchedule option when calling client.workflow.start() with a cron expression like '* * * * *' to start every minute. Cron Schedules follow the format: minute (0-59), hour (0-23), day of month (1-31), month (1-12), day of week (0-6 Sunday to Saturday). Schedules are now recommended instead of Cron Jobs as they provide better developer experience and more configuration options.
Start Delay for one-time future Workflow execution
Use the startDelay option to schedule a Workflow Execution at a specific one-time future point rather than on a recurring schedule. The startDelay option can be specified on either client.workflow.start() or client.workflow.execute() methods with a duration string like '2 hours'.
Create a Schedule example in TypeScript
Example of creating a scheduled workflow:
```typescript
const schedule = await client.schedule.create({
action: {
type: 'startWorkflow',
workflowType: reminder,
args: ['♻️ Dear future self, please take out the recycling tonight. Sincerely, past you ❤️'],
taskQueue: 'schedules',
},
scheduleId: 'sample-schedule',
policies: {
catchupWindow: '1 day',
overlap: ScheduleOverlapPolicy.ALLOW_ALL,
},
spec: {
intervals: [{ every: '10s' }],
},
});
```
This creates a schedule that runs a reminder workflow every 10 seconds with a catchup window of 1 day and allows all overlapping executions.
Backfill a Schedule example in TypeScript
Example of backfilling a schedule:
```typescript
function subtractMinutes(minutes: number): Date {
const now = new Date();
return new Date(now.getTime() - minutes * 60 * 1000);
}
const backfillOptions: Backfill = {
start: subtractMinutes(10),
end: subtractMinutes(9),
overlap: ScheduleOverlapPolicy.ALLOW_ALL,
};
const handle = client.schedule.getHandle('sample-schedule');
await handle.backfill(backfillOptions);
```
This backfills the schedule to run actions that would have occurred between 10 and 9 minutes ago.
Delete a Schedule with handle.delete()
To delete a Schedule, use client.schedule.getHandle(scheduleId) to get a handle, then call handle.delete(). Deleting a Schedule does not affect any Workflows that were already started by the Schedule.
Start Delay example with client.workflow.start()
Example of using Start Delay:
```typescript
const handle = await client.workflow.start(someWorkflow, {
// ...
startDelay: '2 hours',
});
```
This schedules the workflow to start 2 hours from now as a one-time execution.
Create a Schedule with client.schedule.create()
To create a Schedule in TypeScript, use client.schedule.create() with an action object specifying the workflow type, arguments, and task queue. The scheduleId is required and uniquely identifies the Schedule. Include a spec object with either intervals (like {every: '10s'}), calendars for periodic times or specific datetimes, or both. The policies object can specify catchupWindow and overlap policy. Once a Schedule completes creating all its Workflow Executions, the Temporal Service automatically deletes it.
Schedule create action fields
The action parameter in schedule.create() requires: type (set to 'startWorkflow'), workflowType, args (array of arguments), and taskQueue. The scheduleId must be provided and is unique. The policies object contains: catchupWindow (duration string like '1 day') and overlap (ScheduleOverlapPolicy enum value like ALLOW_ALL). The spec object contains: intervals (array of {every: duration} objects for repeated intervals) or calendars (array of calendar objects with optional comment, dayOfWeek, hour, minute, year, month, dayOfMonth).
Backfill a Schedule with handle.backfill()
To backfill a Schedule, retrieve a schedule handle using client.schedule.getHandle(scheduleId) and call handle.backfill() with a Backfill options object. The Backfill object requires start and end Date properties, and can specify an overlap policy. Backfill executes Actions ahead of their specified time range, useful for executing missed Actions or testing Workflows before their scheduled time.
Temporal Cron Job example with cronSchedule
Example of starting a workflow with a cron schedule:
```typescript
const handle = await client.workflow.start(scheduledWorkflow, {
// ...
cronSchedule: '* * * * *', // start every minute
});
```
This starts a workflow that will be executed every minute following the cron expression.
Describe a Schedule with handle.describe()
To get detailed information about a Schedule, use client.schedule.getHandle(scheduleId) and call handle.describe(). This returns the current Schedule configuration including information about past, current, and future Workflow Runs.
Workflow failure only by manual ApplicationFailure
You will only fail a Workflow by manually raising an ApplicationFailure from the Workflow code. In a Workflow, any exceptions raised other than an explicit Temporal ApplicationFailure will only fail that particular Workflow Task and be retried. This includes typical TypeScript runtime errors like undefined errors, which are treated as bugs that can be corrected with a fixed deployment.
Catching ActivityFailure in Workflow code
In Workflow code, you can catch ActivityFailure and check if its cause is an ApplicationFailure to handle Activity errors. Example: try { await addAddress(); } catch (err) { if (err instanceof ActivityFailure && err.cause instanceof ApplicationFailure) { log.error(err.cause.message); throw err; } }
Activity Failure never directly causes Workflow Failure
One of the core design principles of Temporal is that an Activity Failure will never directly cause a Workflow Failure. A Workflow should never return as Failed unless deliberately. The default retry policy associated with Temporal Activities is to retry them until reaching a certain timeout threshold. Activities will not return a failure to the Workflow until a timeout condition or another non-retryable condition is met, at which point the Workflow code can decide how to handle the error.
Workflow Retry Policy example in TypeScript
A Retry Policy can be set through the WorkflowOptions.retry property when starting a Workflow. Workflow Executions do not retry by default. Example: client.workflow.start(example, { taskQueue, workflowId, retry: { maximumAttempts: 3, maximumInterval: '30 seconds' } })
Eager Workflow Start requirements
Eager Workflow Start requires the Starter and the Worker to share a Client located in the same process and setting request_eager_start to true in the Start Workflow call. When set, and the Worker has a Workflow Task slot available and the Workflow Definition registered, the Worker can execute the first task of the Workflow locally without first making a round-trip to the Temporal Server.
Eager Workflow Start availability and configuration
Eager Workflow Start is enabled for all Temporal Cloud users and self-hosted Temporal Server 1.29.0+. No additional configuration or access request is needed. However, you must set request_eager_start (or similar name depending on SDK) to true when starting each Workflow for Eager Workflow Start to be used.
Eager Workflow Start fallback behavior
To recover from errors, Eager Workflow Start falls back to the non-eager path. For example, when the first Task is returned eagerly, but the local Worker fails or times out while processing the task, the server retries this task non-eagerly after WorkflowTaskTimeout.
Eager Workflow Start purpose and benefits
Eager Workflow Start reduces the latency required to initiate a Workflow execution. It is recommended for short-lived Workflows that use Local Activities to interact with external services, especially when these interactions are initiated in the first Workflow Task and the Workflow is deployed near the Temporal Server to minimize network delay. This feature is particularly beneficial for Workflows with a happy path that must begin external interactions within tens of milliseconds.
Temporal Workflow Schedule Cron Expression Format
Temporal Workflow Schedule Cron strings follow the standard five-field format:
1. **Minute**: 0-59
2. **Hour**: 0-23
3. **Day of the month**: 1-31
4. **Month**: 1-12
5. **Day of the week**: 0-6 (where 0 is Sunday and 6 is Saturday)
Examples:
- "15 8 * * *" means 8:15 AM every day
- "* * * * *" represents every minute
Activities can be used as alternative to Side Effects
An Activity or a Local Activity can be used instead of a Side Effect for non-deterministic operations, as their results are also persisted in Workflow Execution History.
Check if it's time to Continue-As-New
To determine when to Continue-As-New, call one of these methods depending on your language:
- Java: Workflow::getInfo()->shouldContinueAsNew
- Go: wf.workflowInfo().continueAsNewSuggested
Temporal tracks your Workflow's progress against Event History Limits and sets this flag when you should restart the Workflow via Continue-As-New.