协作头脑风暴
brainstorming
在实现前澄清需求、探索设计选择,并形成清晰的开发方向。
condition-based-waiting【开发与工程】Replace arbitrary timeouts with condition polling for reliable async tests
独立 GitHub 快照 · 自动分类 · 以原作者文档为准
name: condition-based-waiting description: Replace arbitrary timeouts with condition polling for reliable async tests user-invocable: false disable-model-invocation: true when_to_use: when tests have race conditions, timing dependencies, or inconsistent pass/fail behavior version: 1.1.0 languages: all progressive_disclosure: entry_point: summary: "Replace arbitrary timeouts with condition polling for reliable async tests" when_to_use: "Tests with setTimeout/sleep, flaky tests, or timing-dependent async operations" quick_start: | 1. Identify arbitrary delays in tests (setTimeout, sleep, time.sleep()) 2. Replace with condition-based waiting (waitFor pattern) 3. Use domain-specific helpers for common scenarios See @example.ts for complete working implementation core_pattern: | // ❌ Guessing at timing await new Promise(r => setTimeout(r, 50));
// ✅ Waiting for condition
await waitFor(() => getResult() !== undefined);
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.
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:
setTimeout, sleep, time.sleep())Don't use when:
// ❌ 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();
| Scenario | Pattern |
|---|---|
| Wait for event | waitFor(() => events.find(e => e.type === 'DONE')) |
| Wait for state | waitFor(() => machine.state === 'ready') |
| Wait for count | waitFor(() => items.length >= 5) |
| Wait for file | waitFor(() => fs.existsSync(path)) |
| Complex condition | waitFor(() => obj.ready && obj.value > 10) |
Generic polling function:
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 (waitForEvent, waitForEventCount, waitForEventMatch).
For detailed patterns, implementation guide, and common mistakes, see @references/patterns-and-implementation.md
From debugging session (2025-10-03):