Schedule tasks·续2
Use Queue when:
- You need background processing without blocking the response
- Tasks should run ASAP but do not need to block
- Order matters (FIFO)
Use Scheduling when:
- Tasks need to run at a specific time
- You need recurring jobs (cron)
- Delayed execution (debouncing, retries)
Use Workflows when:
- Multi-step processes with dependencies
- Automatic retries with backoff
- Human-in-the-loop approvals
- Long-running tasks (minutes to hours)
API reference
schedule()
async schedule<T>(
when: Date | string | number,
callback: keyof this,
payload?: T,
options?: { retry?: RetryOptions; idempotent?: boolean }
): Promise<Schedule<T>>
Schedule a task for future execution.
Parameters:
when- When to execute:number(seconds delay),Date(specific time), orstring(cron expression)callback- Name of the method to callpayload- Data to pass to the callback (must be JSON-serializable)options.retry- Optional retry configuration. Refer to Retries for detailsoptions.idempotent- Deduplicate by callback + payload. Defaults totruefor cron schedules,falsefor delayed and Date-based schedules
Returns: A Schedule object with the task details
Idempotency:
Cron schedules are idempotent by default — calling schedule("0 * * * *", "tick") multiple times with the same callback, cron expression, and payload returns the existing schedule instead of creating a duplicate. Set idempotent: false to override this.
For delayed and Date-based schedules, set idempotent: true to opt in to the same dedup behavior (matched on callback + payload). This is especially useful when calling schedule() in onStart() to avoid accumulating duplicate rows across Durable Object restarts:
class MyAgent extends Agent {
async onStart() {
// Without idempotent: true, this creates a new row on every DO restart
await this.schedule(3600, "hourlyCleanup", {}, { idempotent: true });
}
}
class MyAgent extends Agent {
async onStart() {
// Without idempotent: true, this creates a new row on every DO restart
await this.schedule(3600, "hourlyCleanup", {}, { idempotent: true });
}
}
Caution
Tasks that set a callback for a method that does not exist will throw an exception. Ensure that the method named in the callback argument exists on your Agent class.
scheduleEvery()
async scheduleEvery<T>(
intervalSeconds: number,
callback: keyof this,
payload?: T,
options?: { retry?: RetryOptions }
): Promise<Schedule<T>>
Schedule a task to run repeatedly at a fixed interval.
Parameters:
intervalSeconds- Number of seconds between executions (must be greater than 0)callback- Name of the method to callpayload- Data to pass to the callback (must be JSON-serializable)options.retry- Optional retry configuration. Refer to Retries for details.
Returns: A Schedule object with type: "interval"
Behavior:
- First execution occurs after
intervalSeconds(not immediately) - If callback is still running when next execution is due, it is skipped (overlap prevention)
- If callback throws an error, the interval continues
- Cancel with
cancelSchedule(id)to stop the entire interval
getScheduleById()
async getScheduleById(id: string): Promise<Schedule<unknown> | undefined>
Get a scheduled task by ID. Returns undefined if not found. This method works in both top-level agents and sub-agents.
listSchedules()
async listSchedules(criteria?: {
id?: string;
type?: "scheduled" | "delayed" | "cron" | "interval";
timeRange?: { start?: Date; end?: Date };
}): Promise<Schedule<unknown>[]>
Get scheduled tasks matching the criteria. This method works in both top-level agents and sub-agents.
getSchedule()
getSchedule<T>(id: string): Schedule<T> | undefined
Deprecated. Get a scheduled task by ID synchronously. This method only works in top-level agents. Use await this.getScheduleById(id) instead.
getSchedules()
getSchedules<T>(criteria?: {
id?: string;
type?: "scheduled" | "delayed" | "cron" | "interval";
timeRange?: { start?: Date; end?: Date };
}): Schedule<T>[]
Deprecated. Get scheduled tasks matching the criteria synchronously. This method only works in top-level agents. Use await this.listSchedules(criteria) instead.
cancelSchedule()
async cancelSchedule(id: string): Promise<boolean>
Cancel a scheduled task. Returns true if cancelled, false if not found.
keepAlive()
async keepAlive(): Promise<() => void>
Prevent the Durable Object from being evicted due to inactivity by holding a 30-second alarm-backed heartbeat reference. Returns a disposer function that releases the heartbeat when called. The disposer is idempotent — calling it multiple times is safe.
Always call the disposer when the work is done — otherwise the heartbeat continues indefinitely.
const dispose = await this.keepAlive();
try {
// Long-running work that must not be interrupted
const result = await longRunningComputation();
await sendResults(result);
} finally {
dispose();
}
const dispose = await this.keepAlive();
try {
// Long-running work that must not be interrupted
const result = await longRunningComputation();
await sendResults(result);
} finally {
dispose();
}
keepAliveWhile()
async keepAliveWhile<T>(fn: () => Promise<T>): Promise<T>
Run an async function while keeping the Durable Object alive. The heartbeat is automatically started before the function runs and stopped when it completes (whether it succeeds or throws). Returns the value returned by the function.
This is the recommended way to use keepAlive — it guarantees cleanup.
const result = await this.keepAliveWhile(async () => {
const data = await longRunningComputation();
return data;
});
const result = await this.keepAliveWhile(async () => {
const data = await longRunningComputation();
return data;
});
Keeping the agent alive
Durable Objects are evicted after a period of inactivity (typically 70-140 seconds with no incoming requests, WebSocket messages, or alarms). During long-running operations — streaming LLM responses, waiting on external APIs, running multi-step computations — the agent can be evicted mid-flight.
keepAlive() prevents this by holding an in-memory heartbeat reference and using the Durable Object alarm system directly. The alarm firing itself resets the inactivity timer.
- The heartbeat does not conflict with your own schedules because the alarm system multiplexes through a single alarm slot.
- No schedule rows are created, and the heartbeat is invisible to
listSchedules(). - Multiple concurrent
keepAlive()calls use a reference count, so one disposer does not release another caller's heartbeat. - Inside sub-agents,
keepAlive()delegates that heartbeat reference to the top-level parent because facets do not have independent alarm slots.
Multiple concurrent callers
Each keepAlive() call returns an independent disposer:
const dispose1 = await this.keepAlive();
const dispose2 = await this.keepAlive();
// Both heartbeats are active
dispose1(); // Only cancels the first heartbeat
// Agent is still alive via dispose2's heartbeat
dispose2(); // Now the agent can go idle
const dispose1 = await this.keepAlive();
const dispose2 = await this.keepAlive();
// Both heartbeats are active
dispose1(); // Only cancels the first heartbeat
// Agent is still alive via dispose2's heartbeat
dispose2(); // Now the agent can go idle
AIChatAgent
AIChatAgent automatically calls keepAlive() during streaming responses. You do not need to add it yourself when using AIChatAgent — every LLM stream is protected from idle eviction by default.
When to use keepAlive
| Scenario | Use keepAlive? |
|---|---|
Streaming LLM responses via AIChatAgent |
No — already built in |
| Long-running computation in a custom Agent | Yes |
| Waiting on a slow external API call | Yes |
| Multi-step tool execution | Yes |
| Short request-response handlers | No — not needed |
| Background work via scheduling or workflows | No — alarms already keep the DO active |
Limits
- Maximum tasks: Limited by SQLite storage (each task is a row). Practical limit is tens of thousands per agent.
- Task size: Each task (including payload) can be up to 2MB.
- Minimum delay: 0 seconds (runs on next alarm tick)
- Cron precision: Minute-level (not seconds)
- Interval precision: Second-level
- Cron jobs: After execution, automatically rescheduled for the next occurrence
- Interval jobs: After execution, rescheduled for
now + intervalSeconds; skipped if still running
Next steps
Push notifications
Send browser push notifications using scheduling and web-push.
Queue tasks
Immediate background task processing.
Run Workflows
Durable multi-step background processing.
Agents API
Complete API reference for the Agents SDK.
Was this helpful?
YesNo
On this page
{"@context":"https://schema.org","@type":"TechArticle","@id":"https://developers.cloudflare.com/agents/runtime/execution/schedule-tasks/#page","headline":"Schedule tasks","description":"Schedule delayed, date-based, cron, and interval tasks on Agents with persistent SQLite-backed execution.","url":"https://developers.cloudflare.com/agents/runtime/execution/schedule-tasks/","inLanguage":"en","image":"https://developers.cloudflare.com/agents/runtime/execution/schedule-tasks/og.png?v=dc9be7068b6b2c24","dateModified":"2026-10-18","publisher":{"@type":"Organization","name":"Cloudflare","description":"One platform for your apps, agents, and workforce. Build, secure, and scale without managing infrastructure","url":"https://www.cloudflare.com/","sameAs":["https://github.com/cloudflare","https://www.linkedin.com/company/cloudflare","https://x.com/cloudflare"],"logo":{"@type":"ImageObject","url":"https://developers.cloudflare.com/logo.svg"},"address":{"@type":"PostalAddress","streetAddress":"101 Townsend St","addressLocality":"San Francisco","addressRegion":"CA","postalCode":"94107","addressCountry":"US"},"contactPoint":[{"@type":"ContactPoint","contactType":"Customer Support","url":"https://support.cloudflare.com/","availableLanguage":["English"]},{"@type":"ContactPoint","contactType":"Sales","url":"https://www.cloudflare.com/contact/","availableLanguage":["English"]}]},"isPartOf":{"@type":"WebSite","@id":"https://developers.cloudflare.com/#website","name":"Cloudflare Docs","url":"https://developers.cloudflare.com/"}}
description: Spawn child agents with isolated storage and typed RPC using subAgent(), abortSubAgent(), and deleteSubAgent(). title: Sub-agents image: https://developers.cloudflare.com/agents/runtime/execution/sub-agents/og.png?v=1d540fe8d5fdfbbf
Documentation Index
Fetch the complete documentation index at: https://developers.cloudflare.com/agents/llms.txt
Use this file to discover all available pages before exploring further.
Docs