cloudflare-workflows
Compare original and translation side by side
🇺🇸
Original
English🇨🇳
Translation
ChineseCloudflare Workflows
Cloudflare Workflows
Status: Production Ready ✅ (GA since April 2025)
Last Updated: 2026-01-09
Dependencies: cloudflare-worker-base (for Worker setup)
Latest Versions: wrangler@4.58.0, @cloudflare/workers-types@4.20260109.0
Recent Updates (2025):
- April 2025: Workflows GA release - waitForEvent API, Vitest testing, CPU time metrics, 4,500 concurrent instances
- October 2025: Instance creation rate 10x faster (100/sec), concurrency increased to 10,000
- 2025 Limits: Max steps 1,024, state persistence 1MB/step (100MB-1GB per instance), event payloads 1MB, CPU time 5 min max
- Testing: cloudflare:test module with introspectWorkflowInstance, disableSleeps, mockStepResult, mockEvent modifiers
- Platform: Waiting instances don't count toward concurrency, retention 3-30 days, subrequests 50-1,000
状态:已就绪可用于生产环境 ✅(2025年4月正式发布)
最后更新:2026-01-09
依赖项:cloudflare-worker-base(用于Worker设置)
最新版本:wrangler@4.58.0, @cloudflare/workers-types@4.20260109.0
2025年近期更新:
- 2025年4月:Workflows正式发布 - 新增waitForEvent API、Vitest测试、CPU时间指标、支持4500个并发实例
- 2025年10月:实例创建速度提升10倍(每秒100个),并发上限提升至10000个
- 2025年限制:最大步骤数1024,每步骤状态持久化1MB(每个实例100MB-1GB),事件负载1MB,最大CPU时间5分钟
- 测试:cloudflare:test模块包含introspectWorkflowInstance、disableSleeps、mockStepResult、mockEvent等修饰器
- 平台特性:等待状态的实例不计入并发数,状态保留3-30天,子请求数50-1000
Quick Start (5 Minutes)
快速开始(5分钟)
bash
undefinedbash
undefined1. Scaffold project
1. 初始化项目
npm create cloudflare@latest my-workflow -- --template cloudflare/workflows-starter --git --deploy false
cd my-workflow
npm create cloudflare@latest my-workflow -- --template cloudflare/workflows-starter --git --deploy false
cd my-workflow
2. Configure wrangler.jsonc
2. 配置wrangler.jsonc
{
"name": "my-workflow",
"main": "src/index.ts",
"compatibility_date": "2025-11-25",
"workflows": [{
"name": "my-workflow",
"binding": "MY_WORKFLOW",
"class_name": "MyWorkflow"
}]
}
{
"name": "my-workflow",
"main": "src/index.ts",
"compatibility_date": "2025-11-25",
"workflows": [{
"name": "my-workflow",
"binding": "MY_WORKFLOW",
"class_name": "MyWorkflow"
}]
}
3. Create workflow (src/index.ts)
3. 创建工作流(src/index.ts)
import { WorkflowEntrypoint, WorkflowStep, WorkflowEvent } from 'cloudflare:workers';
export class MyWorkflow extends WorkflowEntrypoint<Env, Params> {
async run(event: WorkflowEvent<Params>, step: WorkflowStep) {
const result = await step.do('process', async () => { /* work / });
await step.sleep('wait', '1 hour');
await step.do('continue', async () => { / more work */ });
}
}
import { WorkflowEntrypoint, WorkflowStep, WorkflowEvent } from 'cloudflare:workers';
export class MyWorkflow extends WorkflowEntrypoint<Env, Params> {
async run(event: WorkflowEvent<Params>, step: WorkflowStep) {
const result = await step.do('process', async () => { /* 业务逻辑 / });
await step.sleep('wait', '1 hour');
await step.do('continue', async () => { / 更多业务逻辑 */ });
}
}
4. Deploy and test
4. 部署并测试
npm run deploy
npx wrangler workflows instances list my-workflow
**CRITICAL**: Extends `WorkflowEntrypoint`, implements `run()` with `step` methods, bindings in wrangler.jsonc
---npm run deploy
npx wrangler workflows instances list my-workflow
**关键注意事项**:继承`WorkflowEntrypoint`,实现带有`step`方法的`run()`,在wrangler.jsonc中配置绑定
---Known Issues Prevention
已知问题预防
This skill prevents 12 documented errors with Cloudflare Workflows.
本技能可预防Cloudflare Workflows的12种已记录错误。
Issue #1: waitForEvent Skips Events After Timeout in Local Dev
问题1:本地开发中waitForEvent超时后跳过事件
Error: Events sent after a timeout are ignored in subsequent calls
Environment: Local development () only - works correctly in production
Source: GitHub Issue #11740
waitForEvent()waitForEvent()wrangler devWhy It Happens: Bug in miniflare that was fixed in production (May 2025) but not ported to local emulator. After a timeout, the event queue becomes corrupted for that instance.
Prevention:
- Test waitForEvent timeout scenarios in production/staging, not local dev
- Avoid chaining multiple calls where timeouts are expected
waitForEvent()
Example of Bug:
typescript
export class MyWorkflow extends WorkflowEntrypoint<Env, Params> {
async run(event: WorkflowEvent<Params>, step: WorkflowStep) {
for (let i = 0; i < 3; i++) {
try {
const evt = await step.waitForEvent(`wait-${i}`, {
type: 'user-action',
timeout: '5 seconds'
});
console.log(`Iteration ${i}: Received event`);
} catch {
console.log(`Iteration ${i}: Timeout`);
}
}
}
}
// In wrangler dev:
// - Iteration 1: ✅ receives event
// - Iteration 2: ⏱️ times out (expected)
// - Iteration 3: ❌ does not receive event (BUG - event is sent but ignored)Status: Known bug, fix pending for miniflare.
错误现象:超时后发送的事件会在后续调用中被忽略
环境:仅本地开发环境()- 生产环境正常
来源:GitHub Issue #11740
waitForEvent()waitForEvent()wrangler dev原因:miniflare中的bug已在生产环境修复(2025年5月),但未移植到本地模拟器。超时后,该实例的事件队列会损坏。
预防方案:
- 在生产/预发布环境测试waitForEvent超时场景,不要在本地开发环境测试
- 避免在预期会超时的场景中链式调用多个
waitForEvent()
错误示例:
typescript
export class MyWorkflow extends WorkflowEntrypoint<Env, Params> {
async run(event: WorkflowEvent<Params>, step: WorkflowStep) {
for (let i = 0; i < 3; i++) {
try {
const evt = await step.waitForEvent(`wait-${i}`, {
type: 'user-action',
timeout: '5 seconds'
});
console.log(`第${i}次循环:收到事件`);
} catch {
console.log(`第${i}次循环:超时`);
}
}
}
}
// 在wrangler dev中:
// - 第1次循环:✅ 收到事件
// - 第2次循环:⏱️ 超时(符合预期)
// - 第3次循环:❌ 未收到事件(BUG - 事件已发送但被忽略)状态:已知bug,等待miniflare修复。
Issue #2: getPlatformProxy() Fails With Workflow Bindings
问题2:getPlatformProxy()与工作流绑定时失败
Error:
Message: Worker's binding refers to service with named entrypoint, but service has no such entrypoint
Source: GitHub Issue #9402
MiniflareCoreError [ERR_RUNTIME_FAILURE]: The Workers runtime failed to startWhy It Happens: from package doesn't support Workflow bindings (similar to how it handles Durable Objects). This blocks Next.js integration and local CLI scripts.
getPlatformProxy()wranglerPrevention:
- Option 1: Comment out workflow bindings when using
getPlatformProxy() - Option 2: Create separate without workflows for CLI scripts
wrangler.cli.jsonc - Option 3: Access workflow bindings directly via deployed worker, not proxy
typescript
// Workaround: Separate config for CLI scripts
// wrangler.cli.jsonc (no workflows)
{
"name": "my-worker",
"main": "src/index.ts",
"compatibility_date": "2025-01-20"
// workflows commented out
}
// Use in script:
import { getPlatformProxy } from 'wrangler';
const { env } = await getPlatformProxy({ configPath: './wrangler.cli.jsonc' });Status: Known limitation, fix planned (filter workflows similar to DOs).
错误信息:
提示信息:Worker的绑定引用了带有命名入口点的服务,但该服务没有此入口点
来源:GitHub Issue #9402
MiniflareCoreError [ERR_RUNTIME_FAILURE]: The Workers runtime failed to start原因:wrangler包中的不支持工作流绑定(与Durable Objects的处理方式类似)。这会阻碍Next.js集成和本地CLI脚本。
getPlatformProxy()预防方案:
- 方案1:使用时注释掉工作流绑定
getPlatformProxy() - 方案2:创建单独的,不含工作流配置供CLI脚本使用
wrangler.cli.jsonc - 方案3:直接通过已部署的Worker访问工作流绑定,而非通过代理
typescript
// 临时解决方案:为CLI脚本使用单独配置
// wrangler.cli.jsonc(不含工作流)
{
"name": "my-worker",
"main": "src/index.ts",
"compatibility_date": "2025-01-20"
// 工作流配置已注释
}
// 在脚本中使用:
import { getPlatformProxy } from 'wrangler';
const { env } = await getPlatformProxy({ configPath: './wrangler.cli.jsonc' });状态:已知限制,计划修复(与DOs类似过滤工作流)。
Issue #3: Workflow Instance Lost After Immediate Redirect (Local Dev)
问题3:本地开发中立即重定向后工作流实例丢失
Error: Instance ID returned but when queried
Environment: Local development () only - works correctly in production
Source: GitHub Issue #10806
instance.not_foundwrangler devWhy It Happens: Returning a redirect immediately after causes request to "soft abort" before workflow initialization completes (single-threaded execution in dev).
workflow.create()Prevention: Use to ensure workflow initialization completes before redirect:
ctx.waitUntil()typescript
export default {
async fetch(req: Request, env: Env, ctx: ExecutionContext): Promise<Response> {
const workflow = await env.MY_WORKFLOW.create({ params: { userId: '123' } });
// ✅ Ensure workflow initialization completes
ctx.waitUntil(workflow.status());
return Response.redirect('/dashboard', 302);
}
};Status: Fixed in recent wrangler versions (post-Sept 2025), but workaround still recommended for compatibility.
原因:调用后立即返回重定向会导致请求在工作流初始化完成前"软中止"(开发环境为单线程执行)。
workflow.create()预防方案:使用确保工作流初始化完成后再重定向:
ctx.waitUntil()typescript
export default {
async fetch(req: Request, env: Env, ctx: ExecutionContext): Promise<Response> {
const workflow = await env.MY_WORKFLOW.create({ params: { userId: '123' } });
// ✅ 确保工作流初始化完成
ctx.waitUntil(workflow.status());
return Response.redirect('/dashboard', 302);
}
};状态:在近期wrangler版本(2025年9月后)已修复,但仍建议使用该兼容方案。
Issue #4: Vitest Tests Unreliable in CI Environments
问题4:CI环境中Vitest测试不可靠
Error:
Environment: CI/CD pipelines (GitLab, GitHub Actions) - works locally
Source: GitHub Issue #10600
[vitest-worker]: Timeout calling "resolveId"Why It Happens: has resource constraint issues in CI containers, affecting workflow tests more than other worker types.
@cloudflare/vitest-pool-workersPrevention:
- Increase in vitest config:
testTimeouttypescriptexport default defineWorkersConfig({ test: { testTimeout: 60_000 // Default: 5000ms } }); - Check CI resource limits (CPU/memory)
- Use if not testing storage isolation
isolatedStorage: false - Consider testing against deployed instances instead of vitest for critical workflows
Status: Known issue, investigating (Internal: WOR-945).
错误信息:
环境:CI/CD流水线(GitLab、GitHub Actions)- 本地环境正常
来源:GitHub Issue #10600
[vitest-worker]: Timeout calling "resolveId"原因:在CI容器中存在资源限制问题,对工作流测试的影响大于其他Worker类型。
@cloudflare/vitest-pool-workers预防方案:
- 在vitest配置中增加:
testTimeouttypescriptexport default defineWorkersConfig({ test: { testTimeout: 60_000 // 默认:5000ms } }); - 检查CI资源限制(CPU/内存)
- 若未测试存储隔离,使用
isolatedStorage: false - 对于关键工作流,考虑针对已部署实例测试而非使用vitest
状态:已知问题,正在调查(内部编号:WOR-945)。
Issue #5: Instance restart() and terminate() Not Implemented in Local Dev
问题5:本地开发中未实现instance restart()和terminate()
Error: when calling or
Environment: Local development () only - works in production
Source: GitHub Issue #11312
Error: Not implemented yetinstance.restart()instance.terminate()wrangler devWhy It Happens: Instance management APIs not yet implemented in miniflare. Additionally, instance status shows even when workflow is sleeping.
runningPrevention: Test instance lifecycle management (pause/resume/terminate) in production or staging environment until local dev support is added.
typescript
const instance = await env.MY_WORKFLOW.get(instanceId);
// ❌ Fails in wrangler dev
await instance.restart(); // Error: Not implemented yet
await instance.terminate(); // Error: Not implemented yet
// ✅ Works in productionStatus: Known limitation, no timeline for local dev support.
错误信息:调用或时提示
环境:仅本地开发环境()- 生产环境正常
来源:GitHub Issue #11312
instance.restart()instance.terminate()Error: Not implemented yetwrangler dev原因:miniflare尚未实现实例管理API。此外,即使工作流处于睡眠状态,实例状态仍显示为。
running预防方案:在生产或预发布环境测试实例生命周期管理(暂停/恢复/终止),直到本地开发环境支持该功能。
typescript
const instance = await env.MY_WORKFLOW.get(instanceId);
// ❌ 在wrangler dev中失败
await instance.restart(); // 错误:Not implemented yet
await instance.terminate(); // 错误:Not implemented yet
// ✅ 在生产环境正常工作状态:已知限制,暂无本地开发环境支持时间表。
Issue #6: I/O Must Be Inside step.do() Callbacks
问题6:I/O操作必须在step.do()回调内执行
Error:
Source: Cloudflare runtime behavior
"Cannot perform I/O on behalf of a different request"Why It Happens: Trying to use I/O objects created in one request context from another request handler.
Prevention: Always perform I/O within callbacks:
step.do()typescript
// ❌ Bad - I/O outside step
const response = await fetch('https://api.example.com/data');
const data = await response.json();
await step.do('use data', async () => {
return data; // This will fail!
});
// ✅ Good - I/O inside step
const data = await step.do('fetch data', async () => {
const response = await fetch('https://api.example.com/data');
return await response.json();
});错误信息:
来源:Cloudflare运行时行为
"Cannot perform I/O on behalf of a different request"原因:尝试在一个请求上下文中创建的I/O对象用于另一个请求处理程序。
预防方案:始终在回调内执行I/O操作:
step.do()typescript
// ❌ 错误示例 - I/O操作在step外执行
const response = await fetch('https://api.example.com/data');
const data = await response.json();
await step.do('use data', async () => {
return data; // 此操作会失败!
});
// ✅ 正确示例 - I/O操作在step内执行
const data = await step.do('fetch data', async () => {
const response = await fetch('https://api.example.com/data');
return await response.json();
});Issue #7: NonRetryableError Behaves Differently in Dev vs Production
问题7:NonRetryableError在开发与生产环境行为不同
Error: NonRetryableError with empty message causes retries in dev mode but works correctly in production
Environment: Development-specific bug
Source: GitHub Issue #10113
Why It Happens: Empty error messages are handled differently between miniflare and production runtime.
Prevention: Always provide a message to NonRetryableError:
typescript
// ❌ Retries in dev, exits in prod
throw new NonRetryableError('');
// ✅ Exits in both environments
throw new NonRetryableError('Validation failed');Status: Known issue, workaround documented.
错误现象:空消息的NonRetryableError在开发环境中会重试,但在生产环境中正常退出
环境:仅开发环境bug
来源:GitHub Issue #10113
原因:miniflare与生产运行时对空错误消息的处理方式不同。
预防方案:始终为NonRetryableError提供错误消息:
typescript
// ❌ 开发环境重试,生产环境退出
throw new NonRetryableError('');
// ✅ 两种环境均正常退出
throw new NonRetryableError('验证失败');状态:已知问题,已记录临时解决方案。
Issue #8: In-Memory State Lost on Hibernation
问题8:休眠时内存状态丢失
Error: Variables declared outside reset to initial values after sleep/hibernation
Source: Cloudflare Workflows Rules
step.do()Why It Happens: Workflows hibernate when the engine detects no pending work. All in-memory state is lost during hibernation.
Prevention: Only use state returned from - everything else is ephemeral:
step.do()typescript
// ❌ BAD - In-memory variable lost on hibernation
let counter = 0;
export class MyWorkflow extends WorkflowEntrypoint<Env, Params> {
async run(event: WorkflowEvent<Params>, step: WorkflowStep) {
counter = await step.do('increment', async () => counter + 1);
await step.sleep('wait', '1 hour'); // ← Hibernates here, in-memory state lost
console.log(counter); // ❌ Will be 0, not 1!
}
}
// ✅ GOOD - State from step.do() return values persists
export class MyWorkflow extends WorkflowEntrypoint<Env, Params> {
async run(event: WorkflowEvent<Params>, step: WorkflowStep) {
const counter = await step.do('increment', async () => 1);
await step.sleep('wait', '1 hour');
console.log(counter); // ✅ Still 1
}
}错误现象:在外声明的变量在睡眠/休眠后重置为初始值
来源:Cloudflare Workflows规则
step.do()原因:当引擎检测到无待处理工作时,工作流会进入休眠状态。休眠期间所有内存状态都会丢失。
预防方案:仅使用返回的状态 - 其他所有状态都是临时的:
step.do()typescript
// ❌ 错误示例 - 内存变量在休眠时丢失
let counter = 0;
export class MyWorkflow extends WorkflowEntrypoint<Env, Params> {
async run(event: WorkflowEvent<Params>, step: WorkflowStep) {
counter = await step.do('increment', async () => counter + 1);
await step.sleep('wait', '1 hour'); // ← 此处进入休眠,内存状态丢失
console.log(counter); // ❌ 结果为0,而非1!
}
}
// ✅ 正确示例 - step.do()返回的状态会持久化
export class MyWorkflow extends WorkflowEntrypoint<Env, Params> {
async run(event: WorkflowEvent<Params>, step: WorkflowStep) {
const counter = await step.do('increment', async () => 1);
await step.sleep('wait', '1 hour');
console.log(counter); // ✅ 结果仍为1
}
}Issue #9: Non-Deterministic Step Names Break Caching
问题9:非确定性步骤名称破坏缓存
Error: Steps re-run unnecessarily, performance degradation
Source: Cloudflare Workflows Rules
Why It Happens: Step names act as cache keys. Using , , or other non-deterministic values causes new cache keys every run.
Date.now()Math.random()Prevention: Use static, deterministic step names:
typescript
// ❌ BAD - Non-deterministic step name
await step.do(`fetch-data-${Date.now()}`, async () => {
return await fetchExpensiveData();
});
// Every execution creates new cache key → step always re-runs
// ✅ GOOD - Deterministic step name
await step.do('fetch-data', async () => {
return await fetchExpensiveData();
});
// Same cache key → result reused on restart/retry错误现象:步骤不必要地重新运行,导致性能下降
来源:Cloudflare Workflows规则
原因:步骤名称作为缓存键。使用、或其他非确定性值会导致每次运行生成新的缓存键。
Date.now()Math.random()预防方案:使用静态、确定性的步骤名称:
typescript
// ❌ 错误示例 - 非确定性步骤名称
await step.do(`fetch-data-${Date.now()}`, async () => {
return await fetchExpensiveData();
});
// 每次执行都会创建新的缓存键 → 步骤始终重新运行
// ✅ 正确示例 - 确定性步骤名称
await step.do('fetch-data', async () => {
return await fetchExpensiveData();
});
// 使用相同的缓存键 → 重启/重试时复用结果Issue #10: Promise.race/any Outside step.do() Causes Inconsistency
问题10:step.do()外的Promise.race/any导致不一致
Error: Different promises resolve on restart, inconsistent behavior
Source: Cloudflare Workflows Rules
Why It Happens: Non-deterministic operations outside steps run again on restart, potentially with different results.
Prevention: Keep all non-deterministic logic inside :
step.do()typescript
// ❌ BAD - Race outside step
const fastest = await Promise.race([fetchA(), fetchB()]);
await step.do('use result', async () => fastest);
// On restart: race runs again, different promise might win
// ✅ GOOD - Race inside step
const fastest = await step.do('fetch fastest', async () => {
return await Promise.race([fetchA(), fetchB()]);
});
// On restart: cached result used, consistent behavior错误现象:重启后不同的Promise可能会resolve,导致行为不一致
来源:Cloudflare Workflows规则
原因:步骤外的非确定性操作在重启时会再次运行,可能产生不同的结果。
预防方案:将所有非确定性逻辑放在内:
step.do()typescript
// ❌ 错误示例 - 竞争操作在step外执行
const fastest = await Promise.race([fetchA(), fetchB()]);
await step.do('use result', async () => fastest);
// 重启时:竞争操作再次运行,可能会有不同的Promise获胜
// ✅ 正确示例 - 竞争操作在step内执行
const fastest = await step.do('fetch fastest', async () => {
return await Promise.race([fetchA(), fetchB()]);
});
// 重启时:使用缓存结果,行为一致Issue #11: Side Effects Repeat on Restart
问题11:重启时副作用重复执行
Error: Duplicate logs, metrics, or operations after workflow restart
Source: Cloudflare Workflows Rules
Why It Happens: Code outside executes multiple times if the workflow restarts mid-execution.
step.do()Prevention: Put logging, metrics, and other side effects inside :
step.do()typescript
// ❌ BAD - Side effect outside step
console.log('Workflow started'); // ← Logs multiple times on restart
await step.do('work', async () => { /* work */ });
// ✅ GOOD - Side effects inside step
await step.do('log start', async () => {
console.log('Workflow started'); // ← Logs once (cached)
});错误现象:工作流重启后出现重复日志、指标或操作
来源:Cloudflare Workflows规则
原因:如果工作流在执行中途重启,外的代码会多次执行。
step.do()预防方案:将日志、指标和其他副作用放在内:
step.do()typescript
// ❌ 错误示例 - 副作用在step外执行
console.log('工作流已启动'); // ← 重启时会多次打印日志
await step.do('work', async () => { /* 业务逻辑 */ });
// ✅ 正确示例 - 副作用在step内执行
await step.do('log start', async () => {
console.log('工作流已启动'); // ← 仅打印一次(已缓存)
});Issue #12: Non-Idempotent Operations Can Repeat
问题12:非幂等操作可能重复执行
Error: Double charges, duplicate database writes after step timeout
Source: Cloudflare Workflows Rules
Why It Happens: Steps retry individually. If an API call succeeds but the step times out before returning, the retry will call the API again.
Prevention: Guard non-idempotent operations with existence checks:
typescript
// ❌ BAD - Charge customer without check
await step.do('charge', async () => {
return await stripe.charges.create({ amount: 1000, customer: customerId });
});
// If step times out after charge succeeds, retry charges AGAIN!
// ✅ GOOD - Check for existing charge first
await step.do('charge', async () => {
const existing = await stripe.charges.list({ customer: customerId, limit: 1 });
if (existing.data.length > 0) return existing.data[0]; // Idempotent
return await stripe.charges.create({ amount: 1000, customer: customerId });
});错误现象:步骤超时后出现重复收费、重复数据库写入等情况
来源:Cloudflare Workflows规则
原因:步骤会单独重试。如果API调用成功但步骤在返回前超时,重试会再次调用该API。
预防方案:为非幂等操作添加存在性检查:
typescript
// ❌ 错误示例 - 未检查直接收费
await step.do('charge', async () => {
return await stripe.charges.create({ amount: 1000, customer: customerId });
});
// 如果收费成功后步骤超时,重试会再次收费!
// ✅ 正确示例 - 先检查是否已存在收费记录
await step.do('charge', async () => {
const existing = await stripe.charges.list({ customer: customerId, limit: 1 });
if (existing.data.length > 0) return existing.data[0]; // 幂等操作
return await stripe.charges.create({ amount: 1000, customer: customerId });
});Step Methods
步骤方法
step.do() - Execute Work
step.do() - 执行业务逻辑
typescript
step.do<T>(name: string, config?: WorkflowStepConfig, callback: () => Promise<T>): Promise<T>Parameters:
- - Step name (for observability)
name - (optional) - Retry configuration (retries, timeout, backoff)
config - - Async function that does the work
callback
Returns: Value from callback (must be serializable)
Example:
typescript
const result = await step.do('call API', { retries: { limit: 10, delay: '10s', backoff: 'exponential' }, timeout: '5 min' }, async () => {
return await fetch('https://api.example.com/data').then(r => r.json());
});CRITICAL - Serialization:
- ✅ Allowed: string, number, boolean, Array, Object, null
- ❌ Forbidden: Function, Symbol, circular references, undefined
- Throws error if return value isn't JSON serializable
typescript
step.do<T>(name: string, config?: WorkflowStepConfig, callback: () => Promise<T>): Promise<T>参数:
- - 步骤名称(用于可观测性)
name - (可选)- 重试配置(retries、timeout、backoff)
config - - 执行业务逻辑的异步函数
callback
返回值: 回调函数的返回值(必须可序列化)
示例:
typescript
const result = await step.do('call API', { retries: { limit: 10, delay: '10s', backoff: 'exponential' }, timeout: '5 min' }, async () => {
return await fetch('https://api.example.com/data').then(r => r.json());
});关键注意事项 - 序列化:
- ✅ 允许类型:string、number、boolean、Array、Object、null
- ❌ 禁止类型:Function、Symbol、循环引用、undefined
- 如果返回值不可序列化为JSON,会抛出错误
step.sleep() - Relative Sleep
step.sleep() - 相对延迟
typescript
step.sleep(name: string, duration: WorkflowDuration): Promise<void>Parameters:
- - Step name
name - - Number (ms) or string:
duration,"second","minute","hour","day","week","month"(plural forms accepted)"year"
Examples:
typescript
await step.sleep('wait 5 minutes', '5 minutes');
await step.sleep('wait 1 hour', '1 hour');
await step.sleep('wait 2 days', '2 days');
await step.sleep('wait 30 seconds', 30000); // millisecondsNote: Resuming workflows take priority over new instances. Sleeps don't count toward step limits.
typescript
step.sleep(name: string, duration: WorkflowDuration): Promise<void>参数:
- - 步骤名称
name - - 数字(毫秒)或字符串:
duration、"second"、"minute"、"hour"、"day"、"week"、"month"(支持复数形式)"year"
示例:
typescript
await step.sleep('wait 5 minutes', '5 minutes');
await step.sleep('wait 1 hour', '1 hour');
await step.sleep('wait 2 days', '2 days');
await step.sleep('wait 30 seconds', 30000); // 毫秒注意:恢复工作流的优先级高于新实例。sleep操作不计入步骤限制。
step.sleepUntil() - Sleep to Specific Date
step.sleepUntil() - 延迟到指定时间
typescript
step.sleepUntil(name: string, timestamp: Date | number): Promise<void>Parameters:
- - Step name
name - - Date object or UNIX timestamp (milliseconds)
timestamp
Examples:
typescript
await step.sleepUntil('wait for launch', new Date('2025-12-25T00:00:00Z'));
await step.sleepUntil('wait until time', Date.parse('24 Oct 2024 13:00:00 UTC'));typescript
step.sleepUntil(name: string, timestamp: Date | number): Promise<void>参数:
- - 步骤名称
name - - Date对象或UNIX时间戳(毫秒)
timestamp
示例:
typescript
await step.sleepUntil('wait for launch', new Date('2025-12-25T00:00:00Z'));
await step.sleepUntil('wait until time', Date.parse('24 Oct 2024 13:00:00 UTC'));step.waitForEvent() - Wait for External Event (GA April 2025)
step.waitForEvent() - 等待外部事件(2025年4月正式发布)
typescript
step.waitForEvent<T>(name: string, options: { type: string; timeout?: string | number }): Promise<T>Parameters:
- - Step name
name - - Event type to match
options.type - (optional) - Max wait time (default: 24 hours, max: 30 days)
options.timeout
Returns: Event payload sent via
instance.sendEvent()Example:
typescript
export class PaymentWorkflow extends WorkflowEntrypoint<Env, Params> {
async run(event: WorkflowEvent<Params>, step: WorkflowStep) {
await step.do('create payment', async () => { /* Stripe API */ });
const webhookData = await step.waitForEvent<StripeWebhook>(
'wait for payment confirmation',
{ type: 'stripe-webhook', timeout: '1 hour' }
);
if (webhookData.status === 'succeeded') {
await step.do('fulfill order', async () => { /* fulfill */ });
}
}
}
// Worker sends event to workflow
export default {
async fetch(req: Request, env: Env): Promise<Response> {
if (req.url.includes('/webhook/stripe')) {
const instance = await env.PAYMENT_WORKFLOW.get(instanceId);
await instance.sendEvent({ type: 'stripe-webhook', payload: await req.json() });
return new Response('OK');
}
}
};Timeout handling:
typescript
try {
const event = await step.waitForEvent('wait for user', { type: 'user-submitted', timeout: '10 minutes' });
} catch (error) {
await step.do('send reminder', async () => { /* reminder */ });
}typescript
step.waitForEvent<T>(name: string, options: { type: string; timeout?: string | number }): Promise<T>参数:
- - 步骤名称
name - - 要匹配的事件类型
options.type - (可选)- 最大等待时间(默认:24小时,最大值:30天)
options.timeout
返回值: 通过发送的事件负载
instance.sendEvent()示例:
typescript
export class PaymentWorkflow extends WorkflowEntrypoint<Env, Params> {
async run(event: WorkflowEvent<Params>, step: WorkflowStep) {
await step.do('create payment', async () => { /* Stripe API调用 */ });
const webhookData = await step.waitForEvent<StripeWebhook>(
'wait for payment confirmation',
{ type: 'stripe-webhook', timeout: '1 hour' }
);
if (webhookData.status === 'succeeded') {
await step.do('fulfill order', async () => { /* 订单履约 */ });
}
}
}
// Worker向工作流发送事件
export default {
async fetch(req: Request, env: Env): Promise<Response> {
if (req.url.includes('/webhook/stripe')) {
const instance = await env.PAYMENT_WORKFLOW.get(instanceId);
await instance.sendEvent({ type: 'stripe-webhook', payload: await req.json() });
return new Response('OK');
}
}
};超时处理:
typescript
try {
const event = await step.waitForEvent('wait for user', { type: 'user-submitted', timeout: '10 minutes' });
} catch (error) {
await step.do('send reminder', async () => { /* 发送提醒 */ });
}WorkflowStepConfig
WorkflowStepConfig
typescript
interface WorkflowStepConfig {
retries?: {
limit: number; // Max attempts (Infinity allowed)
delay: string | number; // Delay between retries
backoff?: 'constant' | 'linear' | 'exponential';
};
timeout?: string | number; // Max time per attempt
}Default:
{ retries: { limit: 5, delay: 10000, backoff: 'exponential' }, timeout: '10 minutes' }Backoff Examples:
typescript
// Constant: 30s, 30s, 30s
{ retries: { limit: 3, delay: '30 seconds', backoff: 'constant' } }
// Linear: 1m, 2m, 3m, 4m, 5m
{ retries: { limit: 5, delay: '1 minute', backoff: 'linear' } }
// Exponential (recommended): 10s, 20s, 40s, 80s, 160s
{ retries: { limit: 10, delay: '10 seconds', backoff: 'exponential' }, timeout: '5 minutes' }
// Unlimited retries
{ retries: { limit: Infinity, delay: '1 minute', backoff: 'exponential' } }
// No retries
{ retries: { limit: 0 } }typescript
interface WorkflowStepConfig {
retries?: {
limit: number; // 最大尝试次数(允许Infinity)
delay: string | number; // 重试间隔
backoff?: 'constant' | 'linear' | 'exponential';
};
timeout?: string | number; // 每次尝试的最大时间
}默认配置:
{ retries: { limit: 5, delay: 10000, backoff: 'exponential' }, timeout: '10 minutes' }退避策略示例:
typescript
// 固定间隔:30s, 30s, 30s
{ retries: { limit: 3, delay: '30 seconds', backoff: 'constant' } }
// 线性增长:1m, 2m, 3m, 4m, 5m
{ retries: { limit: 5, delay: '1 minute', backoff: 'linear' } }
// 指数增长(推荐):10s, 20s, 40s, 80s, 160s
{ retries: { limit: 10, delay: '10 seconds', backoff: 'exponential' }, timeout: '5 minutes' }
// 无限重试
{ retries: { limit: Infinity, delay: '1 minute', backoff: 'exponential' } }
// 不重试
{ retries: { limit: 0 } }Error Handling
错误处理
NonRetryableError
NonRetryableError
Force workflow to fail immediately without retrying:
typescript
import { WorkflowEntrypoint, WorkflowStep, WorkflowEvent } from 'cloudflare:workers';
import { NonRetryableError } from 'cloudflare:workflows';
export class MyWorkflow extends WorkflowEntrypoint<Env, Params> {
async run(event: WorkflowEvent<Params>, step: WorkflowStep) {
await step.do('validate input', async () => {
if (!event.payload.userId) {
throw new NonRetryableError('userId is required');
}
// Validate user exists
const user = await this.env.DB.prepare(
'SELECT * FROM users WHERE id = ?'
).bind(event.payload.userId).first();
if (!user) {
// Terminal error - retrying won't help
throw new NonRetryableError('User not found');
}
return user;
});
}
}When to use NonRetryableError:
- ✅ Authentication/authorization failures
- ✅ Invalid input that won't change
- ✅ Resource doesn't exist (404)
- ✅ Validation errors
- ❌ Network failures (should retry)
- ❌ Rate limits (should retry with backoff)
- ❌ Temporary service outages (should retry)
强制工作流立即失败,不进行重试:
typescript
import { WorkflowEntrypoint, WorkflowStep, WorkflowEvent } from 'cloudflare:workers';
import { NonRetryableError } from 'cloudflare:workflows';
export class MyWorkflow extends WorkflowEntrypoint<Env, Params> {
async run(event: WorkflowEvent<Params>, step: WorkflowStep) {
await step.do('validate input', async () => {
if (!event.payload.userId) {
throw new NonRetryableError('userId是必填项');
}
// 验证用户是否存在
const user = await this.env.DB.prepare(
'SELECT * FROM users WHERE id = ?'
).bind(event.payload.userId).first();
if (!user) {
// 终端错误 - 重试无济于事
throw new NonRetryableError('用户不存在');
}
return user;
});
}
}NonRetryableError适用场景:
- ✅ 认证/授权失败
- ✅ 不会改变的无效输入
- ✅ 资源不存在(404)
- ✅ 验证错误
- ❌ 网络故障(应重试)
- ❌ 速率限制(应带退避策略重试)
- ❌ 临时服务中断(应重试)
Catch Errors to Continue Workflow
捕获错误以继续工作流
Prevent workflow failure by catching optional step errors:
typescript
export class MyWorkflow extends WorkflowEntrypoint<Env, Params> {
async run(event: WorkflowEvent<Params>, step: WorkflowStep) {
await step.do('process payment', async () => { /* critical */ });
try {
await step.do('send email', async () => { /* optional */ });
} catch (error) {
await step.do('log failure', async () => {
await this.env.DB.prepare('INSERT INTO failed_emails VALUES (?, ?)').bind(event.payload.userId, error.message).run();
});
}
await step.do('update status', async () => { /* continues */ });
}
}Graceful Degradation:
typescript
let result;
try {
result = await step.do('call primary API', async () => await callPrimaryAPI());
} catch {
result = await step.do('call backup API', async () => await callBackupAPI());
}通过捕获可选步骤的错误来防止工作流失败:
typescript
export class MyWorkflow extends WorkflowEntrypoint<Env, Params> {
async run(event: WorkflowEvent<Params>, step: WorkflowStep) {
await step.do('process payment', async () => { /* 核心逻辑 */ });
try {
await step.do('send email', async () => { /* 可选逻辑 */ });
} catch (error) {
await step.do('log failure', async () => {
await this.env.DB.prepare('INSERT INTO failed_emails VALUES (?, ?)').bind(event.payload.userId, error.message).run();
});
}
await step.do('update status', async () => { /* 继续执行 */ });
}
}优雅降级:
typescript
let result;
try {
result = await step.do('call primary API', async () => await callPrimaryAPI());
} catch {
result = await step.do('call backup API', async () => await callBackupAPI());
}Triggering Workflows
触发工作流
Configure binding (wrangler.jsonc):
jsonc
{
"workflows": [{
"name": "my-workflow",
"binding": "MY_WORKFLOW",
"class_name": "MyWorkflow",
"script_name": "workflow-worker" // If workflow in different Worker
}]
}Trigger from Worker:
typescript
const instance = await env.MY_WORKFLOW.create({ params: { userId: '123' } });
return Response.json({ id: instance.id, status: await instance.status() });Instance Management:
typescript
const instance = await env.MY_WORKFLOW.get(instanceId);
const status = await instance.status(); // { status: 'running'|'complete'|'errored'|'queued', error, output }
await instance.sendEvent({ type: 'user-action', payload: { action: 'approved' } });
await instance.pause();
await instance.resume();
await instance.terminate();配置绑定(wrangler.jsonc):
jsonc
{
"workflows": [{
"name": "my-workflow",
"binding": "MY_WORKFLOW",
"class_name": "MyWorkflow",
"script_name": "workflow-worker" // 如果工作流在不同的Worker中
}]
}从Worker触发:
typescript
const instance = await env.MY_WORKFLOW.create({ params: { userId: '123' } });
return Response.json({ id: instance.id, status: await instance.status() });实例管理:
typescript
const instance = await env.MY_WORKFLOW.get(instanceId);
const status = await instance.status(); // { status: 'running'|'complete'|'errored'|'queued', error, output }
await instance.sendEvent({ type: 'user-action', payload: { action: 'approved' } });
await instance.pause();
await instance.resume();
await instance.terminate();State Persistence
状态持久化
Workflows automatically persist state returned from :
step.do()✅ Serializable:
- Primitives: ,
string,number,booleannull - Arrays, Objects, Nested structures
❌ Non-Serializable:
- Functions, Symbols, circular references, undefined, class instances
Example:
typescript
// ✅ Good
const result = await step.do('fetch data', async () => ({
users: [{ id: 1, name: 'Alice' }],
timestamp: Date.now(),
metadata: null
}));
// ❌ Bad - function not serializable
const bad = await step.do('bad', async () => ({ data: [1, 2, 3], transform: (x) => x * 2 })); // Throws error!Access State Across Steps:
typescript
const userData = await step.do('fetch user', async () => ({ id: 123, email: 'user@example.com' }));
const orderData = await step.do('create order', async () => ({ userId: userData.id, orderId: 'ORD-456' }));
await step.do('send email', async () => sendEmail({ to: userData.email, subject: `Order ${orderData.orderId}` }));工作流会自动持久化返回的状态:
step.do()✅ 可序列化类型:
- 基本类型: ,
string,number,booleannull - 数组、对象、嵌套结构
❌ 不可序列化类型:
- 函数、Symbol、循环引用、undefined、类实例
示例:
typescript
// ✅ 正确示例
const result = await step.do('fetch data', async () => ({
users: [{ id: 1, name: 'Alice' }],
timestamp: Date.now(),
metadata: null
}));
// ❌ 错误示例 - 函数不可序列化
const bad = await step.do('bad', async () => ({ data: [1, 2, 3], transform: (x) => x * 2 })); // 会抛出错误!跨步骤访问状态:
typescript
const userData = await step.do('fetch user', async () => ({ id: 123, email: 'user@example.com' }));
const orderData = await step.do('create order', async () => ({ userId: userData.id, orderId: 'ORD-456' }));
await step.do('send email', async () => sendEmail({ to: userData.email, subject: `Order ${orderData.orderId}` }));Observability
可观测性
Built-in Metrics (Enhanced in 2025)
内置指标(2025年增强)
Workflows automatically track:
- Instance status: queued, running, complete, errored, paused, waiting
- Step execution: start/end times, duration, success/failure
- Retry history: attempts, errors, delays
- Sleep state: when workflow will wake up
- Output: return values from steps and run()
- CPU time (GA April 2025): Active processing time per instance for billing insights
工作流会自动跟踪:
- 实例状态: 排队中、运行中、已完成、已错误、已暂停、等待中
- 步骤执行: 开始/结束时间、耗时、成功/失败
- 重试历史: 尝试次数、错误信息、延迟
- 睡眠状态: 工作流唤醒时间
- 输出: 步骤和run()的返回值
- CPU时间(2025年4月正式发布): 每个实例的活跃处理时间,用于计费洞察
View Metrics in Dashboard
在控制台查看指标
Access via Cloudflare dashboard:
- Workers & Pages
- Select your workflow
- View instances and metrics
Metrics include:
- Total instances created
- Success/error rates
- Average execution time
- Step-level performance
- CPU time consumption (2025 feature)
通过Cloudflare控制台访问:
- 进入Workers & Pages
- 选择你的工作流
- 查看实例和指标
指标包含:
- 创建的实例总数
- 成功/错误率
- 平均执行时间
- 步骤级性能
- CPU时间消耗(2025年新增功能)
Programmatic Access
程序化访问
typescript
const instance = await env.MY_WORKFLOW.get(instanceId);
const status = await instance.status();
console.log(status);
// {
// status: 'complete' | 'running' | 'errored' | 'queued' | 'waiting' | 'unknown',
// error: string | null,
// output: { userId: '123', status: 'processed' }
// }CPU Time Configuration (2025):
jsonc
// wrangler.jsonc
{ "limits": { "cpu_ms": 300000 } } // 5 minutes max (default: 30 seconds)typescript
const instance = await env.MY_WORKFLOW.get(instanceId);
const status = await instance.status();
console.log(status);
// {
// status: 'complete' | 'running' | 'errored' | 'queued' | 'waiting' | 'unknown',
// error: string | null,
// output: { userId: '123', status: 'processed' }
// }CPU时间配置(2025年):
jsonc
// wrangler.jsonc
{ "limits": { "cpu_ms": 300000 } } // 最大5分钟(默认:30秒)Limits (Updated 2025)
限制(2025年更新)
| Feature | Workers Free | Workers Paid |
|---|---|---|
| Max steps per workflow | 1,024 | 1,024 |
| Max state per step | 1 MiB | 1 MiB |
| Max state per instance | 100 MB | 1 GB |
| Max event payload size | 1 MiB | 1 MiB |
| Max sleep/sleepUntil duration | 365 days | 365 days |
| Max waitForEvent timeout | 365 days | 365 days |
| CPU time per step | 10 ms | 30 sec (default), 5 min (max) |
| Duration (wall clock) per step | Unlimited | Unlimited |
| Max workflow executions | 100,000/day | Unlimited |
| Concurrent instances | 25 | 10,000 (Oct 2025, up from 4,500) |
| Instance creation rate | 100/second | 100/second (Oct 2025, 10x faster) |
| Max queued instances | 100,000 | 1,000,000 |
| Max subrequests per instance | 50/request | 1,000/request |
| Retention (completed state) | 3 days | 30 days |
| Max Workflow name length | 64 chars | 64 chars |
| Max instance ID length | 100 chars | 100 chars |
CRITICAL Notes:
- and
step.sleep()do NOT count toward 1,024 step limitstep.sleepUntil() - Waiting instances (sleeping, retrying, or waiting for events) do NOT count toward concurrency limits
- Instance creation rate increased 10x (October 2025): 100 per 10 seconds → 100 per second
- Max concurrency increased (October 2025): 4,500 → 10,000 concurrent instances
- State persistence limits increased (2025): 128 KB → 1 MiB per step, 100 MB - 1 GB per instance
- Event payload size increased (2025): 128 KB → 1 MiB
- CPU time configurable via :
wrangler.jsonc(5 min max){ "limits": { "cpu_ms": 300000 } }
| 特性 | Workers免费版 | Workers付费版 |
|---|---|---|
| 每个工作流的最大步骤数 | 1,024 | 1,024 |
| 每步骤的最大状态大小 | 1 MiB | 1 MiB |
| 每个实例的最大状态大小 | 100 MB | 1 GB |
| 最大事件负载大小 | 1 MiB | 1 MiB |
| step.sleep()/step.sleepUntil()最大延迟时间 | 365天 | 365天 |
| waitForEvent()最大超时时间 | 365天 | 365天 |
| 每步骤的CPU时间 | 10 ms | 30秒(默认),5分钟(最大值) |
| 每步骤的挂钟时间 | 无限制 | 无限制 |
| 每日最大工作流执行次数 | 100,000 | 无限制 |
| 并发实例数 | 25 | 10,000(2025年10月,从4,500提升) |
| 实例创建速率 | 100/秒 | 100/秒(2025年10月,提升10倍) |
| 最大排队实例数 | 100,000 | 1,000,000 |
| 每个实例的最大子请求数 | 50/请求 | 1,000/请求 |
| 已完成状态的保留时间 | 3天 | 30天 |
| 工作流名称最大长度 | 64字符 | 64字符 |
| 实例ID最大长度 | 100字符 | 100字符 |
关键注意事项:
- 和
step.sleep()不计入1024步骤限制step.sleepUntil() - 等待状态的实例(睡眠、重试或等待事件)不计入并发数限制
- 实例创建速率在2025年10月提升10倍:从每10秒100个提升至每秒100个
- 最大并发数在2025年10月提升:从4,500提升至10,000个并发实例
- 状态持久化限制在2025年提升:每步骤从128 KB提升至1 MiB,每个实例从100 MB提升至1 GB
- 事件负载大小在2025年提升:从128 KB提升至1 MiB
- CPU时间可通过配置:
wrangler.jsonc(最大5分钟){ "limits": { "cpu_ms": 300000 } }
Pricing
定价
Requires Workers Paid plan ($5/month)
Workflow Executions:
- First 10,000,000 step executions/month: FREE
- After that: $0.30 per million step executions
What counts as a step execution:
- Each call
step.do() - Each retry of a step
- ,
step.sleep(),step.sleepUntil()do NOT countstep.waitForEvent()
Cost examples:
- Workflow with 5 steps, no retries: 5 step executions
- Workflow with 3 steps, 1 step retries 2 times: 5 step executions (3 + 2)
- 10M simple workflows/month (5 steps each): ((50M - 10M) / 1M) × $0.30 = $12/month
需要Workers付费计划(每月5美元)
工作流执行费用:
- 每月前10,000,000次步骤执行:免费
- 超出部分:每百万次步骤执行0.30美元
计入步骤执行的操作:
- 每次调用
step.do() - 步骤的每次重试
- 、
step.sleep()、step.sleepUntil()不计入step.waitForEvent()
费用示例:
- 包含5个步骤、无重试的工作流:5次步骤执行
- 包含3个步骤、其中1个步骤重试2次的工作流:5次步骤执行(3+2)
- 每月1000万个简单工作流(每个5个步骤):((50,000,000 - 10,000,000) / 1,000,000) × 0.30 = 每月12美元
Vitest Testing (GA April 2025)
Vitest测试(2025年4月正式发布)
Workflows support full testing integration via module.
cloudflare:test工作流支持通过模块进行完整测试集成。
cloudflare:testSetup
安装配置
bash
npm install -D vitest@latest @cloudflare/vitest-pool-workers@latestvitest.config.ts:
typescript
import { defineWorkersConfig } from '@cloudflare/vitest-pool-workers/config';
export default defineWorkersConfig({ test: { poolOptions: { workers: { miniflare: { bindings: { MY_WORKFLOW: { scriptName: 'workflow' } } } } } } });bash
npm install -D vitest@latest @cloudflare/vitest-pool-workers@latestvitest.config.ts:
typescript
import { defineWorkersConfig } from '@cloudflare/vitest-pool-workers/config';
export default defineWorkersConfig({ test: { poolOptions: { workers: { miniflare: { bindings: { MY_WORKFLOW: { scriptName: 'workflow' } } } } } });Introspection API
内省API
typescript
import { env, introspectWorkflowInstance } from 'cloudflare:test';
it('should complete workflow', async () => {
const instance = await introspectWorkflowInstance(env.MY_WORKFLOW, 'test-123');
try {
await instance.modify(async (m) => {
await m.disableSleeps(); // Skip all sleeps
await m.mockStepResult({ name: 'fetch data' }, { users: [{ id: 1 }] }); // Mock step result
await m.mockEvent({ type: 'approval', payload: { approved: true } }); // Send mock event
await m.mockStepError({ name: 'call API' }, new Error('Network timeout'), 1); // Force error once
});
await env.MY_WORKFLOW.create({ id: 'test-123' });
await expect(instance.waitForStatus('complete')).resolves.not.toThrow();
} finally {
await instance.dispose(); // Cleanup
}
});typescript
import { env, introspectWorkflowInstance } from 'cloudflare:test';
it('should complete workflow', async () => {
const instance = await introspectWorkflowInstance(env.MY_WORKFLOW, 'test-123');
try {
await instance.modify(async (m) => {
await m.disableSleeps(); // 跳过所有睡眠
await m.mockStepResult({ name: 'fetch data' }, { users: [{ id: 1 }] }); // 模拟步骤结果
await m.mockEvent({ type: 'approval', payload: { approved: true } }); // 发送模拟事件
await m.mockStepError({ name: 'call API' }, new Error('Network timeout'), 1); // 强制步骤报错一次
});
await env.MY_WORKFLOW.create({ id: 'test-123' });
await expect(instance.waitForStatus('complete')).resolves.not.toThrow();
} finally {
await instance.dispose(); // 清理资源
}
});Test Modifiers
测试修饰器
- - Skip sleeps instantly
disableSleeps(steps?) - - Mock step.do() result
mockStepResult(step, result) - - Force step.do() to throw
mockStepError(step, error, times?) - - Send mock event to step.waitForEvent()
mockEvent(event) - - Force step.do() timeout
forceStepTimeout(step, times?) - - Force step.waitForEvent() timeout
forceEventTimeout(step)
- - 立即跳过所有睡眠
disableSleeps(steps?) - - 模拟step.do()的结果
mockStepResult(step, result) - - 强制step.do()抛出错误
mockStepError(step, error, times?) - - 向step.waitForEvent()发送模拟事件
mockEvent(event) - - 强制step.do()超时
forceStepTimeout(step, times?) - - 强制step.waitForEvent()超时
forceEventTimeout(step)
Related Documentation
相关文档
- Cloudflare Workflows Docs: https://developers.cloudflare.com/workflows/
- Get Started Guide: https://developers.cloudflare.com/workflows/get-started/guide/
- Workers API: https://developers.cloudflare.com/workflows/build/workers-api/
- Vitest Testing: https://developers.cloudflare.com/workers/testing/vitest-integration/
- Sleeping and Retrying: https://developers.cloudflare.com/workflows/build/sleeping-and-retrying/
- Events and Parameters: https://developers.cloudflare.com/workflows/build/events-and-parameters/
- Limits: https://developers.cloudflare.com/workflows/reference/limits/
- Pricing: https://developers.cloudflare.com/workflows/platform/pricing/
- Changelog: https://developers.cloudflare.com/workflows/reference/changelog/
- MCP Tool: Use for latest docs
mcp__cloudflare-docs__search_cloudflare_documentation
Last Updated: 2026-01-21
Version: 2.0.0
Changes: Added 12 documented Known Issues (TIER 1-2 research findings): waitForEvent timeout bug, getPlatformProxy failure, redirect instance loss, Vitest CI issues, local dev limitations, state persistence rules, caching gotchas, and idempotency patterns
Maintainer: Jeremy Dawes | jeremy@jezweb.net
- Cloudflare Workflows文档: https://developers.cloudflare.com/workflows/
- 快速开始指南: https://developers.cloudflare.com/workflows/get-started/guide/
- Workers API: https://developers.cloudflare.com/workflows/build/workers-api/
- Vitest测试: https://developers.cloudflare.com/workers/testing/vitest-integration/
- 睡眠与重试: https://developers.cloudflare.com/workflows/build/sleeping-and-retrying/
- 事件与参数: https://developers.cloudflare.com/workflows/build/events-and-parameters/
- 限制: https://developers.cloudflare.com/workflows/reference/limits/
- 定价: https://developers.cloudflare.com/workflows/platform/pricing/
- 更新日志: https://developers.cloudflare.com/workflows/reference/changelog/
- MCP工具: 使用获取最新文档
mcp__cloudflare-docs__search_cloudflare_documentation
最后更新: 2026-01-21
版本: 2.0.0
变更: 新增12种已知问题(TIER 1-2研究成果):waitForEvent超时bug、getPlatformProxy失败、重定向实例丢失、Vitest CI问题、本地开发限制、状态持久化规则、缓存陷阱、幂等模式
维护者: Jeremy Dawes | jeremy@jezweb.net