[ PROMPT_NODE_25538 ]
condition-based-waiting
[ SKILL_DOCUMENTATION ]
# 基于条件的等待
## 概述
不稳定的测试通常会通过任意延迟来猜测时间。这会在快速机器上通过但在负载或 CI 环境下失败的测试中产生竞态条件。
**核心原则:** 等待你关心的实际条件,而不是猜测需要多长时间。
## 何时使用
dot
digraph when_to_use {
"测试是否使用 setTimeout/sleep?" [shape=diamond];
"是否在测试时间行为?" [shape=diamond];
"记录为什么需要超时" [shape=box];
"使用基于条件的等待" [shape=box];
"测试是否使用 setTimeout/sleep?" -> "是否在测试时间行为?" [label="是"];
"是否在测试时间行为?" -> "记录为什么需要超时" [label="是"];
"是否在测试时间行为?" -> "使用基于条件的等待" [label="否"];
}
**使用场景:**
- 测试中有任意延迟(`setTimeout`, `sleep`, `time.sleep()`)
- 测试不稳定(有时通过,负载下失败)
- 并行运行时测试超时
- 等待异步操作完成
**不要使用场景:**
- 测试实际的时间行为(防抖、节流间隔)
- 如果必须使用任意超时,请务必记录原因
## 核心模式
typescript
// ❌ 之前:猜测时间
await new Promise(r => setTimeout(r, 50));
const result = getResult();
expect(result).toBeDefined();
// ✅ 之后:等待条件
await waitFor(() => getResult() !== undefined);
const result = getResult();
expect(result).toBeDefined();
## 快速模式
| 场景 | 模式 |
|----------|---------|
| 等待事件 | `waitFor(() => events.find(e => e.type === 'DONE'))` |
| 等待状态 | `waitFor(() => machine.state === 'ready')` |
| 等待计数 | `waitFor(() => items.length >= 5)` |
| 等待文件 | `waitFor(() => fs.existsSync(path))` |
| 复杂条件 | `waitFor(() => obj.ready && obj.value > 10)` |
## 实现
通用轮询函数:
typescript
async function waitFor(
condition: () => T | undefined | null | false,
description: string,
timeoutMs = 5000
): Promise {
const startTime = Date.now();
while (true) {
const result = condition();
if (result) return result;
if (Date.now() - startTime > timeoutMs) {
throw new Error(`等待 ${description} 在 ${timeoutMs}ms 后超时`);
}
await new Promise(r => setTimeout(r, 10)); // 每 10ms 轮询一次
}
}
查看此目录下的 `condition-based-waiting-example.ts` 以获取包含领域特定辅助函数(`waitForEvent`, `waitForEventCount` 等)的完整实现。