effect-ts-concurrency
3
总安装量
2
周安装量
#55887
全站排名
安装命令
npx skills add https://github.com/mrevanzak/effect-ts-skills --skill effect-ts-concurrency
Agent 安装分布
opencode
2
mcpjam
1
claude-code
1
junie
1
windsurf
1
zencoder
1
Skill 文档
Effect-TS Concurrency
Overview
Effect-TS provides lightweight fibers for high-performance concurrency. The core principle is explicit control: always bound parallelism to prevent resource exhaustion.
When to Use
- Processing large arrays of effects (e.g.,
Effect.all,Effect.forEach) - Rate limiting external API calls or database connections
- Coordinating work between background processes (fibers)
- Signaling completion or state changes across different parts of the app
When NOT to use:
- Simple sequential operations
- When standard
Promise.allis sufficient (though Effect is usually preferred for consistency)
Core Pattern: Bounded Parallelism
Unbounded parallelism is the most common source of “Too many open files” or “Connection timeout” errors.
| Pattern | Implementation | Result |
|---|---|---|
| BAD | Effect.all(effects) |
Unbounded – crashes on large inputs |
| GOOD | Effect.all(effects, { concurrency: 10 }) |
Bounded – safe and predictable |
Quick Reference
| Tool | Purpose | Key Method |
|---|---|---|
| Fiber | Background execution | Effect.fork / Fiber.join |
| Semaphore | Bounded concurrency / Rate limiting | semaphore.withPermits(n) |
| Deferred | One-shot signaling / Promises | Deferred.await / Deferred.succeed |
| concurrency | Option for all, forEach, mapEffect |
`{ concurrency: number |
Implementation
1. Bounded Parallelism (Critical)
Always specify concurrency when processing collections.
import { Effect } from 'effect';
// Process 1000 items, max 10 concurrent
const results = yield* Effect.all(
items.map(processItem),
{ concurrency: 10 }
);
2. Semaphore for Rate Limiting
Use a Semaphore when multiple independent operations must share a global limit.
import { Effect } from 'effect';
const program = Effect.gen(function* () {
const semaphore = yield* Effect.makeSemaphore(5); // Max 5 concurrent
yield* Effect.all(
requests.map((req) =>
semaphore.withPermits(1)(handleRequest(req))
),
{ concurrency: 'unbounded' } // Semaphore controls actual concurrency
);
});
3. Deferred for Signaling
Use Deferred to wait for a specific event or value from another fiber.
import { Deferred, Effect, Fiber } from 'effect';
const program = Effect.gen(function* () {
const signal = yield* Deferred.make<void>();
const worker = yield* Effect.fork(
Effect.gen(function* () {
yield* Deferred.await(signal); // Wait for signal
yield* doWork();
})
);
yield* setup();
yield* Deferred.succeed(signal, undefined); // Trigger worker
yield* Fiber.join(worker);
});
Common Mistakes
- Unbounded parallelism: Forgetting
{ concurrency: n }inEffect.all. - Leaking Fibers: Forking fibers without joining or interrupting them (use
Effect.scopedfor safety). - Deadlocks: Circular dependencies between semaphores or deferreds.
Red Flags – STOP and Start Over
- Using
Effect.allon a large array without{ concurrency: n }. - Using
Promise.allinside an Effect-TS codebase. - Manual
setTimeoutfor rate limiting instead ofEffect.makeRateLimiterorSemaphore.
Rationalization Table
| Excuse | Reality |
|---|---|
| “It’s only 100 items” | 100 items today, 10,000 tomorrow. Bound it now. |
| “The API is fast” | Network latency and server load are unpredictable. |
| “I’ll add concurrency later” | Unbounded parallelism is a ticking time bomb. |
REQUIRED BACKGROUND: See effect-ts-anti-patterns for more on unbounded parallelism.