effect-patterns-scheduling-periodic-tasks
Provides methods for debouncing input events and throttling API requests to control the frequency of task execution.
Install
mkdir -p .claude/skills/effect-patterns-scheduling-periodic-tasks && curl -L -o skill.zip "https://agentskills.codes/api/skills/download/7486" && unzip -o skill.zip -d .claude/skills/effect-patterns-scheduling-periodic-tasks && rm skill.zipInstalls to .claude/skills/effect-patterns-scheduling-periodic-tasks
Activation
This is the description your AI agent reads to decide when to run this skill — the better it matches your request, the more reliably it fires.
Effect-TS patterns for Scheduling Periodic Tasks. Use when working with scheduling periodic tasks in Effect-TS applications.Key capabilities
- →Implement input debouncing for search bars
- →Apply throttling to event streams
- →Wait for signal silence before execution
- →Schedule task frequency for high-traffic events
How it works
Uses Effect-TS scheduling modules and Ref primitives to manage the temporal state of events, delaying or dropping execution based on defined thresholds.
Inputs & outputs
When to use effect-patterns-scheduling-periodic-tasks
- →Debounce search input fields
- →Throttle API call frequency
- →Handle rapid event streams
About this skill
Effect-TS Patterns: Scheduling Periodic Tasks
This skill provides 3 curated Effect-TS patterns for scheduling periodic tasks. Use this skill when working on tasks related to:
- scheduling periodic tasks
- Best practices in Effect-TS applications
- Real-world patterns and solutions
🟡 Intermediate Patterns
Scheduling Pattern 4: Debounce and Throttle Execution
Rule: Use debounce to wait for silence before executing, and throttle to limit execution frequency, both critical for handling rapid events.
Good Example:
This example demonstrates debouncing and throttling for common scenarios.
import { Effect, Schedule, Ref } from "effect";
interface SearchQuery {
readonly query: string;
readonly timestamp: Date;
}
// Simulate API search
const performSearch = (query: string): Effect.Effect<string[]> =>
Effect.gen(function* () {
yield* Effect.log(`[API] Searching for: "${query}"`);
yield* Effect.sleep("100 millis"); // Simulate API delay
return [
`Result 1 for ${query}`,
`Result 2 for ${query}`,
`Result 3 for ${query}`,
];
});
// Main: demonstrate debounce and throttle
const program = Effect.gen(function* () {
console.log(`\n[DEBOUNCE/THROTTLE] Handling rapid events\n`);
// Example 1: Debounce search input
console.log(`[1] Debounced search (wait for silence):\n`);
const searchQueries = ["h", "he", "hel", "hell", "hello"];
const debouncedSearches = yield* Ref.make<Effect.Effect<string[]>[]>([]);
for (const query of searchQueries) {
yield* Effect.log(`[INPUT] User typed: "${query}"`);
// In real app, this would be debounced
yield* Effect.sleep("150 millis"); // User typing
}
// After user stops, execute search
yield* Effect.log(`[DEBOUNCE] User silent for 200ms, executing search`);
const searchResults = yield* performSearch("hello");
yield* Effect.log(`[RESULTS] ${searchResults.length} results found\n`);
// Example 2: Throttle scroll events
console.log(`[2] Throttled scroll handler (max 10/sec):\n`);
const scrollEventCount = yield* Ref.make(0);
const updateCount = yield* Ref.make(0);
// Simulate 100 rapid scroll events
for (let i = 0; i < 100; i++) {
yield* Ref.update(scrollEventCount, (c) => c + 1);
// In real app, scroll handler would be throttled
if (i % 10 === 0) {
// Simulate throttled update (max 10 per second)
yield* Ref.update(updateCount, (c) => c + 1);
}
}
const events = yield* Ref.get(scrollEventCount);
const updates = yield* Ref.get(updateCount);
yield* Effect.log(
`[THROTTLE] ${events} scroll events → ${updates} updates (${(updates / events * 100).toFixed(1)}% update rate)\n`
);
// Example 3: Deduplication
console.log(`[3] Deduplicating rapid events:\n`);
const userClicks = ["click", "click", "click", "dblclick", "click"];
const lastClick = yield* Ref.make<string | null>(null);
const clickCount = yield* Ref.make(0);
for (const click of userClicks) {
const prev = yield* Ref.get(lastClick);
if (click !== prev) {
yield* Effect.log(`[CLICK] Processing: ${click}`);
yield* Ref.update(clickCount, (c) => c + 1);
yield* Ref.set(lastClick, click);
} else {
yield* Effect.log(`[CLICK] Duplicate: ${click} (skipped)`);
}
}
const processed = yield* Ref.get(clickCount);
yield* Effect.log(
`\n[DEDUPE] ${userClicks.length} clicks → ${processed} processed\n`
);
// Example 4: Exponential backoff on repeated errors
console.log(`[4] Throttled retry on errors:\n`);
let retryCount = 0;
const operation = Effect.gen(function* () {
retryCount++;
if (retryCount < 3) {
yield* Effect.fail(new Error("Still failing"));
}
yield* Effect.log(`[SUCCESS] Succeeded on attempt ${retryCount}`);
return "done";
}).pipe(
Effect.retry(
Schedule.exponential("100 millis").pipe(
Schedule.upTo("1 second"),
Schedule.recurs(5)
)
)
);
yield* operation;
});
Effect.runPromise(program);
Rationale:
Debounce and throttle manage rapid events:
- Debounce: Wait for silence (delay after last event), then execute once
- Throttle: Execute at most once per interval
- Deduplication: Skip duplicate events
- Rate limiting: Limit events per second
Pattern: Schedule.debounce(duration) or Schedule.throttle(maxEvents, duration)
Rapid events without debounce/throttle cause problems:
Debounce example: Search input
- User types "hello" character by character
- Without debounce: 5 API calls (one per character)
- With debounce: 1 API call after user stops typing
Throttle example: Scroll events
- Scroll fires 100+ times per second
- Without throttle: Updates lag, GC pressure
- With throttle: Update max 60 times per second
Real-world issues:
- API overload: Search queries hammer backend
- Rendering lag: Too many DOM updates
- Resource exhaustion: Event handlers never catch up
Debounce/throttle enable:
- Efficiency: Fewer operations
- Responsiveness: UI stays smooth
- Resource safety: Prevent exhaustion
- Sanity: Predictable execution
Scheduling Pattern 3: Schedule Tasks with Cron Expressions
Rule: Use cron expressions to schedule periodic tasks at specific calendar times, enabling flexible scheduling beyond simple fixed intervals.
Good Example:
This example demonstrates scheduling a daily report generation using cron, with timezone support.
import { Effect, Schedule, Console } from "effect";
import { DateTime } from "luxon"; // For timezone handling
interface ReportConfig {
readonly cronExpression: string;
readonly timezone?: string;
readonly jobName: string;
}
interface ScheduledReport {
readonly timestamp: Date;
readonly jobName: string;
readonly result: string;
}
// Simple cron parser (in production, use a library like cron-parser)
const parseCronExpression = (
expression: string
): {
minute: number[];
hour: number[];
dayOfMonth: number[];
month: number[];
dayOfWeek: number[];
} => {
const parts = expression.split(" ");
const parseField = (field: string, max: number): number[] => {
if (field === "*") {
return Array.from({ length: max + 1 }, (_, i) => i);
}
if (field.includes(",")) {
return field.split(",").flatMap((part) => parseField(part, max));
}
if (field.includes("-")) {
const [start, end] = field.split("-").map(Number);
return Array.from({ length: end - start + 1 }, (_, i) => start + i);
}
return [Number(field)];
};
return {
minute: parseField(parts[0], 59),
hour: parseField(parts[1], 23),
dayOfMonth: parseField(parts[2], 31),
month: parseField(parts[3], 12),
dayOfWeek: parseField(parts[4], 6),
};
};
// Check if current time matches cron expression
const shouldRunNow = (parsed: ReturnType<typeof parseCronExpression>): boolean => {
const now = new Date();
return (
parsed.minute.includes(now.getUTCMinutes()) &&
parsed.hour.includes(now.getUTCHours()) &&
parsed.dayOfMonth.includes(now.getUTCDate()) &&
parsed.month.includes(now.getUTCMonth() + 1) &&
parsed.dayOfWeek.includes(now.getUTCDay())
);
};
// Generate a report
const generateReport = (jobName: string): Effect.Effect<ScheduledReport> =>
Effect.gen(function* () {
yield* Console.log(`[REPORT] Generating ${jobName}...`);
// Simulate report generation
yield* Effect.sleep("100 millis");
return {
timestamp: new Date(),
jobName,
result: `Report generated at ${new Date().toISOString()}`,
};
});
// Schedule with cron expression
const scheduleWithCron = (config: ReportConfig) =>
Effect.gen(function* () {
const parsed = parseCronExpression(config.cronExpression);
yield* Console.log(
`[SCHEDULER] Scheduling job: ${config.jobName}`
);
yield* Console.log(`[SCHEDULER] Cron: ${config.cronExpression}`);
yield* Console.log(`[SCHEDULER] Timezone: ${config.timezone || "UTC"}\n`);
// Create schedule that checks every minute
const schedule = Schedule.fixed("1 minute").pipe(
Schedule.untilInputEffect((report: ScheduledReport) =>
Effect.gen(function* () {
const isPastTime = shouldRunNow(parsed);
if (isPastTime) {
yield* Console.log(
`[SCHEDULED] ✓ Running at ${report.timestamp.toISOString()}`
);
return true; // Stop scheduling
}
return false; // Continue scheduling
})
)
);
// Generate report with cron schedule
yield* generateReport(config.jobName).pipe(
Effect.repeat(schedule)
);
});
// Demonstrate multiple cron schedules
const program = Effect.gen(function* () {
console.log(
`\n[START] Scheduling multiple jobs with cron expressions\n`
);
// Schedule examples (note: in real app, these would run at actual times)
const jobs = [
{
cronExpression: "0 9 * * 1-5", // 9 AM weekdays
jobName: "Daily Standup Report",
timezone: "America/New_York",
},
{
cronExpression: "0 0 * * *", // Midnight daily
jobName: "Nightly Backup",
timezone: "UTC",
},
{
cronExpression: "0 0 1 * *", // Midnight on 1st of month
jobName: "Monthly Summary",
timezone: "Europe/London",
},
];
yield* Console.log("[JOBS] Scheduled:");
jobs.forEach((job) => {
console.log(
` - ${job.jobName}: ${job.cronExpression} (${job.timezone})`
);
});
});
Effect.runPromise(program);
Rationale:
Use cron expressions for scheduling that aligns with business calendars:
- Hourly backups:
0 * * * *(at :00 every hour) - Daily reports:
0 9 * * 1-5(9 AM weekdays) - Monthly cleanup:
0 0 1 * *(midnight on 1st of month) - Business hours:
0 9-17 * * 1-5(9 AM-5 PM, Mon-Fri)
Format: minute hour day month weekday
Fixed interval
Content truncated.
When not to use it
- →Single-shot execution tasks
- →Extremely low-frequency events
- →When native browser APIs like requestAnimationFrame suffice
Prerequisites
Limitations
- →Requires understanding of Effect state management
- →Cannot handle high-throughput event processing beyond memory limits
- →Timing may be slightly impacted by event loop lag
How it compares
It provides a purely functional approach to stateful scheduling without side-effect-heavy manual timers.
Compared to similar skills
effect-patterns-scheduling-periodic-tasks side by side with the closest alternatives in the catalog.
| Skill | Installs | Updated | Safety | Difficulty |
|---|---|---|---|---|
| effect-patterns-scheduling-periodic-tasks (this skill) | 1 | 7mo | No flags | Intermediate |
| drizzle | 238 | 2mo | No flags | Intermediate |
| zustand | 113 | 2mo | No flags | Intermediate |
| motion-canvas | 58 | 6mo | Review | Advanced |
Try saying
Example prompts that trigger this skill in your AI assistant.
More by PaulJPhilp
View all by PaulJPhilp →You might also like
drizzle
lobehub
Drizzle ORM schema and database guide. Use when working with database schemas (src/database/schemas/*), defining tables, creating migrations, or database model code. Triggers on Drizzle schema definition, database migrations, or ORM usage questions.
zustand
lobehub
Zustand state management guide. Use when working with store code (src/store/**), implementing actions, managing state, or creating slices. Triggers on Zustand store development, state management questions, or action implementation.
motion-canvas
davila7
Complete production-ready guide for Motion Canvas with ESM/CommonJS workarounds, full setup templates, and troubleshooting for programmatic video creation using TypeScript
turborepo
vercel
Turborepo monorepo build system guidance. Triggers on: turbo.json, task pipelines, dependsOn, caching, remote cache, the "turbo" CLI, --filter, --affected, CI optimization, environment variables, internal packages, monorepo structure/best practices, and boundaries. Use when user: configures tasks/workflows/pipelines, creates packages, sets up monorepo, shares code between apps, runs changed/affected packages, debugs cache, or has apps/packages directories.
shadcn-ui-setup
maneeshanif
Install and configure Shadcn/ui component library with Radix UI primitives, Aceternity UI effects, set up components, and manage the component registry. Use when adding Shadcn/ui to a Next.js project or installing specific UI components for Phase 2.
angular
sickn33
Modern Angular (v20+) expert with deep knowledge of Signals, Standalone Components, Zoneless applications, SSR/Hydration, and reactive patterns. Use PROACTIVELY for Angular development, component architecture, state management, performance optimization, and migration to modern patterns.