This module is ESM 🔆. Please read this.
An accurate timer utility for running periodic tasks on the given interval ticks or dates — with a single timer instance, zero runtime dependencies, and full TypeScript types.
📖 Full documentation & guides: onury.io/tasktimer
Tip
v4 is a 2026 modernization — ESM-only, zero-dependency, browser-safe, drift-free precision — that also squashed some long-standing bugs and made API improvements, plus new sugar: leading-edge runs (lead), typed task data, typed events, coded errors (TaskTimerError), and silentErrors. What's changed →
Because of the single-threaded, asynchronous nature of JavaScript, each execution takes a slice of CPU time, and the wait before the next one varies with the load. This creates a cumulative latency in naive timers that gradually drifts away from the intended schedule. TaskTimer corrects this drift on every tick, and it lets you run many tasks — each on its own interval, run limit, or date window — from a single timer.
- Precision (on by default): the delay between ticks is auto-adjusted when it drifts due to task/CPU load or clock drift. It uses the monotonic
performance.now()(drift-free, in Node and the browser) and auto-recovers via immediate ticks after a blocking task. - Run or schedule multiple tasks on a single timer instance.
- Sync or async tasks — return a
Promiseor use thedone()callback. - Limit runs per task (
totalRuns), add an initial delay (tickDelay), run on the leading edge (lead), or bind a task to a date window (startDate/stopDate). - Attach arbitrary
datato a task — typed viaTask<TData>. - Add, remove, reset, enable/disable, pause and resume tasks at any time — without recreating the timer.
- Stateful: auto-stop when all tasks complete (
stopOnCompleted); free memory when a task finishes (removeOnCompleted). - A familiar, typed
EventEmittersurface (on/once/off/emit…) — listeners get a typed event. - Coded errors — every throw is a
TaskTimerErrorwith a stableerr.code; opt out of swallowing task errors withsilentErrors. - ESM-only, zero runtime dependencies, runs in Node and the browser, written in TypeScript.
npm i tasktimerimport { TaskTimer, Event, State } from 'tasktimer';Event, State, Task, TaskTimerError and ErrorCode are all named exports (there is no TaskTimer.Event namespace).
Note
TaskTimer is ESM-only. It runs in Node and the browser via native ESM or a bundler (Vite, esbuild, Rollup, webpack …) — precision uses the universal performance.now(), and setImmediate falls back to setTimeout off-Node.
const timer = new TaskTimer(1000); // base interval: 1000 ms
timer.add(task => console.log(`Run #${task.currentRuns}`)).start();const timer = new TaskTimer(5000);
timer.on(Event.TICK, () => console.log(`Tick #${timer.tickCount}`));
timer.start();const timer = new TaskTimer(1000); // 1s base resolution
timer.add([
{
id: 'task-1',
tickInterval: 5, // every 5 ticks → 5s
totalRuns: 10, // run 10 times only (0 = unlimited)
callback(task) {
console.log(`${task.id} ran ${task.currentRuns} times`);
}
},
{
id: 'task-2',
tickDelay: 1, // wait 1 tick before the first run
tickInterval: 10, // every 10 ticks → 10s
totalRuns: 2,
callback(task) {
console.log(`${task.id} ran ${task.currentRuns} times`);
}
}
]);
timer.on(Event.TICK, () => {
console.log(`tick ${timer.tickCount} · elapsed ${timer.time.elapsed} ms`);
});
timer.start();// return a Promise
timer.add(task => fetch(url).then(handle));
// or call done() when finished
timer.add((task, done) => {
fs.readFile(path, () => done());
});Tip
Set defer: true on a task to defer its callback to the next event-loop turn (via setImmediate) — useful when the task synchronously blocks the event loop without doing I/O. Set lead: true to run a task once immediately on start() (the leading edge), instead of waiting a full interval.
const timer = new TaskTimer({ interval: 1000, stopOnCompleted: true });
timer.add({ totalRuns: 3, callback: doWork });
timer.add({ totalRuns: 5, callback: doOtherWork });
timer.on(Event.COMPLETED, () => console.log('all tasks done'));
timer.start();timer.start();
timer.pause(); // holds all tasks
timer.resume(); // continues where it left off
timer.stop(); // stops; tasks and counters are retained
timer.reset(); // back to idle; tasks removed silently- You create a timer with a base interval (e.g.
1000ms) — the tick resolution shared by all tasks. - You add tasks that run on tick intervals (e.g. every 5th tick), optionally with a run limit, an initial delay, or a start/stop date.
- Beyond task callbacks, you can listen for lifecycle events (
tick,task,completed, …). - Tasks can be added, removed, reset, enabled or disabled at any time; the timer can be paused and resumed — all without recreating it.
options is either an ITaskTimerOptions object or a number (the base interval in ms).
| Property | Type | Description |
|---|---|---|
interval |
number |
Base tick interval in ms (read/write). |
precision |
boolean |
Whether drift auto-correction is enabled (read/write). |
stopOnCompleted |
boolean |
Auto-stop once all tasks complete (read/write). |
silentErrors |
boolean |
Swallow task errors with no taskError listener; false surfaces them (read/write). |
state |
State |
Current timer state (read-only). |
time |
ITimeInfo |
{ started, stopped, elapsed } for the current run (read-only). |
tickCount |
number |
Ticks elapsed in the current run (read-only). |
taskCount |
number |
Number of tasks (read-only). |
tasks |
Task[] |
All tasks, in insertion order (read-only). |
taskRunCount |
number |
Total task executions (read-only). |
runCount |
number |
Total timer runs, including resumes (read-only). |
| Method | Returns | Description |
|---|---|---|
add(task) |
TaskTimer |
Add a task, options, callback, or an array of these. |
get(id) |
Task | undefined |
Get a task by id (undefined if absent). |
remove(task) |
TaskTimer |
Remove a task by id or instance. |
start() |
TaskTimer |
Start (or restart) the timer. |
pause() |
TaskTimer |
Pause the timer and all tasks. |
resume() |
TaskTimer |
Resume a paused timer (starts it if idle). |
stop() |
TaskTimer |
Stop the timer, retaining tasks and counters. |
reset() |
TaskTimer |
Stop and reset to idle, removing all tasks silently. |
TaskTimer also exposes the EventEmitter surface: on / addListener, once, off / removeListener, removeAllListeners, emit, listeners, listenerCount, eventNames.
A Task is created implicitly via timer.add(...), or explicitly with the constructor (an ITaskOptions with a required id and callback).
| Member | Type | Description |
|---|---|---|
id |
string |
Unique task id (read-only). |
enabled |
boolean |
While false, the task bypasses its callback (read/write). |
tickDelay |
number |
Ticks to wait before the first run (read/write). |
tickInterval |
number |
Tick interval the task runs on (read/write). |
totalRuns |
number | null |
Run limit; 0/null = unlimited (read/write). |
defer |
boolean |
Defer the callback to the next event-loop turn via setImmediate (read/write). |
lead |
boolean |
Run once immediately on start() (the leading edge) (read/write). |
removeOnCompleted |
boolean |
Remove the task once completed (read/write). |
data |
TData |
Arbitrary user data attached to the task (read/write). |
currentRuns |
number |
Number of times run so far (read-only). |
completed |
boolean |
Whether the task is completed (read-only). |
time |
ITimeInfo |
The task's lifetime { started, stopped, elapsed } (read-only). |
callback |
TaskCallback |
The callback executed on each run (read-only). |
reset(options?) |
Task |
Reset the run count, optionally re-configuring (id can't change). |
All are named exports: import { State, Event, ErrorCode } from 'tasktimer'.
State — IDLE · RUNNING · PAUSED · STOPPED.
ErrorCode — the code on a thrown TaskTimerError: NO_TASK_PROVIDED · TASK_ID_REQUIRED · CALLBACK_REQUIRED · DUPLICATE_TASK_ID · NO_SUCH_TASK · INVALID_DATE_RANGE · CANNOT_CHANGE_ID · TASK_ERROR.
Event — the events emitted by the timer:
| Event | Value | Emitted when |
|---|---|---|
TICK |
tick |
Each tick of the timer. |
STARTED |
started |
The timer is started. |
RESUMED |
resumed |
The timer is resumed. |
PAUSED |
paused |
The timer is paused. |
STOPPED |
stopped |
The timer is stopped. |
RESET |
reset |
The timer is reset. |
TASK |
task |
A task is executed. |
TASK_ADDED |
taskAdded |
A task is added. |
TASK_REMOVED |
taskRemoved |
A task is removed. |
TASK_COMPLETED |
taskCompleted |
A task completes its runs / reaches its stopDate. |
TASK_ERROR |
taskError |
A task throws or rejects. |
COMPLETED |
completed |
Every task has completed. |
Event listeners receive a typed ITaskTimerEvent: { name, timer, task?, error? }. The related task is event.task and the timer is event.timer — on every event, including taskError.
{ interval?, precision?, stopOnCompleted?, silentErrors? }
{ id?, enabled?, tickDelay?, tickInterval?, totalRuns?, startDate?, stopDate?, defer?, lead?, removeOnCompleted?, data?, callback }
{ started, stopped, elapsed } — timestamps and elapsed time in ms.
{ name, timer, task?, error? }
(task: Task<TData>, done?: () => void) => void | Promise<unknown>
Full reference: onury.io/tasktimer.
See CHANGELOG.md. Migrating from v3? See the migration notes — v4 is ESM-only, drops the TaskTimer.Event namespace for named exports, and reshapes the event payload.
- AccessControl — Role and Attribute based Access Control for Node.js.
- Configuard — Turn flat config rows from a database table into a nested, typed configuration object — with
${...}templating and accessor-based (ABAC) filtering. - Notation — Read, modify, and filter the contents of objects and arrays via dot/bracket notation strings or glob patterns.
© 2026, Onur Yıldırım. MIT License.