condition-based-waiting
Compare original and translation side by side
🇺🇸
Original
English🇨🇳
Translation
ChineseCondition-Based Waiting
基于条件的等待
Overview
概述
Flaky tests often guess at timing with arbitrary delays. This creates race conditions where tests pass on fast machines but fail under load or in CI.
Core principle: Wait for the actual condition you care about, not a guess about how long it takes.
不稳定测试通常会通过任意延迟来猜测时序。这会导致竞态条件,使得测试在快速机器上能通过,但在负载下或CI环境中失败。
核心原则: 等待你真正关心的实际条件,而非猜测所需时长。
When to Use
使用场景
dot
digraph when_to_use {
"Test uses setTimeout/sleep?" [shape=diamond];
"Testing timing behavior?" [shape=diamond];
"Document WHY timeout needed" [shape=box];
"Use condition-based waiting" [shape=box];
"Test uses setTimeout/sleep?" -> "Testing timing behavior?" [label="yes"];
"Testing timing behavior?" -> "Document WHY timeout needed" [label="yes"];
"Testing timing behavior?" -> "Use condition-based waiting" [label="no"];
}Use when:
- Tests have arbitrary delays (,
setTimeout,sleep)time.sleep() - Tests are flaky (pass sometimes, fail under load)
- Tests timeout when run in parallel
- Waiting for async operations to complete
Don't use when:
- Testing actual timing behavior (debounce, throttle intervals)
- Always document WHY if using arbitrary timeout
dot
digraph when_to_use {
"Test uses setTimeout/sleep?" [shape=diamond];
"Testing timing behavior?" [shape=diamond];
"Document WHY timeout needed" [shape=box];
"Use condition-based waiting" [shape=box];
"Test uses setTimeout/sleep?" -> "Testing timing behavior?" [label="yes"];
"Testing timing behavior?" -> "Document WHY timeout needed" [label="yes"];
"Testing timing behavior?" -> "Use condition-based waiting" [label="no"];
}适用场景:
- 测试使用了任意延迟(、
setTimeout、sleep)time.sleep() - 测试结果不稳定(时而通过,负载下失败)
- 并行运行时测试超时
- 等待异步操作完成
不适用场景:
- 测试实际时序行为(防抖、节流间隔)
- 若必须使用任意超时,务必记录原因
Core Pattern
核心模式
typescript
// ❌ BEFORE: Guessing at timing
await new Promise(r => setTimeout(r, 50));
const result = getResult();
expect(result).toBeDefined();
// ✅ AFTER: Waiting for condition
await waitFor(() => getResult() !== undefined);
const result = getResult();
expect(result).toBeDefined();typescript
// ❌ 之前:猜测时序
await new Promise(r => setTimeout(r, 50));
const result = getResult();
expect(result).toBeDefined();
// ✅ 之后:等待条件满足
await waitFor(() => getResult() !== undefined);
const result = getResult();
expect(result).toBeDefined();Quick Patterns
快速模式参考
| Scenario | Pattern |
|---|---|
| Wait for event | |
| Wait for state | |
| Wait for count | |
| Wait for file | |
| Complex condition | |
| 场景 | 模式 |
|---|---|
| 等待事件 | |
| 等待状态 | |
| 等待数量达标 | |
| 等待文件生成 | |
| 复杂条件 | |
Implementation
实现方式
Generic polling function:
typescript
async function waitFor<T>(
condition: () => T | undefined | null | false,
description: string,
timeoutMs = 5000
): Promise<T> {
const startTime = Date.now();
while (true) {
const result = condition();
if (result) return result;
if (Date.now() - startTime > timeoutMs) {
throw new Error(`Timeout waiting for ${description} after ${timeoutMs}ms`);
}
await new Promise(r => setTimeout(r, 10)); // Poll every 10ms
}
}See @example.ts for complete implementation with domain-specific helpers (, , ) from actual debugging session.
waitForEventwaitForEventCountwaitForEventMatch通用轮询函数:
typescript
async function waitFor<T>(
condition: () => T | undefined | null | false,
description: string,
timeoutMs = 5000
): Promise<T> {
const startTime = Date.now();
while (true) {
const result = condition();
if (result) return result;
if (Date.now() - startTime > timeoutMs) {
throw new Error(`Timeout waiting for ${description} after ${timeoutMs}ms`);
}
await new Promise(r => setTimeout(r, 10)); // 每10ms轮询一次
}
}查看@example.ts获取完整实现,包含实际调试会话中用到的领域特定辅助函数(、、)。
waitForEventwaitForEventCountwaitForEventMatchCommon Mistakes
常见错误
❌ Polling too fast: - wastes CPU
✅ Fix: Poll every 10ms
setTimeout(check, 1)❌ No timeout: Loop forever if condition never met
✅ Fix: Always include timeout with clear error
❌ Stale data: Cache state before loop
✅ Fix: Call getter inside loop for fresh data
❌ 轮询过于频繁: - 浪费CPU
✅ 修复方案: 每10ms轮询一次
setTimeout(check, 1)❌ 未设置超时: 若条件始终不满足会无限循环
✅ 修复方案: 始终包含超时设置并给出清晰错误信息
❌ 数据过期: 在循环前缓存状态
✅ 修复方案: 在循环内部调用获取函数以获取最新数据
When Arbitrary Timeout IS Correct
何时可以使用任意超时
typescript
// Tool ticks every 100ms - need 2 ticks to verify partial output
await waitForEvent(manager, 'TOOL_STARTED'); // First: wait for condition
await new Promise(r => setTimeout(r, 200)); // Then: wait for timed behavior
// 200ms = 2 ticks at 100ms intervals - documented and justifiedRequirements:
- First wait for triggering condition
- Based on known timing (not guessing)
- Comment explaining WHY
typescript
// 工具每100ms执行一次——需要等待2个周期以验证部分输出
await waitForEvent(manager, 'TOOL_STARTED'); // 第一步:等待触发条件
await new Promise(r => setTimeout(r, 200)); // 第二步:等待时序行为
// 200ms = 100ms间隔的2个周期——已记录并说明合理性要求:
- 先等待触发条件
- 基于已知时序(而非猜测)
- 添加注释说明原因
Real-World Impact
实际业务影响
From debugging session (2025-10-03):
- Fixed 15 flaky tests across 3 files
- Pass rate: 60% → 100%
- Execution time: 40% faster
- No more race conditions
来自调试会话(2025-10-03):
- 修复了3个文件中的15个不稳定测试
- 通过率:60% → 100%
- 执行时间:缩短40%
- 不再出现竞态条件