---
id: gh-condition-based-waiting
name: "Condition-Based Waiting"
url: https://skills.yangsir.net/skill/gh-condition-based-waiting
author: obra
domain: testing
tags: ["testing", "async", "reliability", "automation", "test-automation"]
install_count: 520
rating: 4.20 (120 reviews)
github: https://github.com/obra/superpowers-skills/tree/main/skills/testing/condition-based-waiting
---

# Condition-Based Waiting

> 此技能通过条件轮询替代异步测试中的任意固定延迟，确保测试只在所需条件满足时继续执行。它显著提升了测试的稳定性、可靠性，减少了偶发性失败，并加快了测试执行速度，是解决异步操作测试痛点的关键。

**Stats**: 520 installs · 4.2/5 (120 reviews)

## Before / After 对比

### 测试稳定性与效率提升

**Before**:

在没有此技能之前，开发者在编写异步测试时，常被迫使用固定的 `setTimeout` 或 `sleep` 来猜测等待时间，这导致测试结果不稳定，容易出现偶发性失败，尤其在不同环境或负载下，严重影响开发效率和测试可靠性。

**After**:

该 Skill 通过条件轮询机制，使测试能够智能地等待特定条件满足后再继续执行，彻底消除了因猜测等待时间而导致的测试不稳定性，显著提升了测试的通过率和执行效率，确保了测试结果的可靠性。

| Metric | Before | After | Change |
|---|---|---|---|
| 测试通过率 | 60% | 100% | +67% |
| 测试执行时间 | 100% | 60% | -40% |
| 偶发性失败数量 | 15个 | 0个 | -100% |

## Readme

# Condition-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.

## 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

## 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();
```

## Quick Patterns

| 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)` |

## 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 (`waitForEvent`, `waitForEventCount`, `waitForEventMatch`) from actual debugging session.

## Common Mistakes

**❌ Polling too fast:** `setTimeout(check, 1)` - wastes CPU
**✅ Fix:** Poll every 10ms

**❌ 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

## 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 justified
```

**Requirements:**
1. First wait for triggering condition
2. Based on known timing (not guessing)
3. Comment explaining WHY

## 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


---
*Source: https://skills.yangsir.net/skill/gh-condition-based-waiting*
*Markdown mirror: https://skills.yangsir.net/api/skill/gh-condition-based-waiting/markdown*