View all files | ||||
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.
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.
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.
options is either an ITaskTimerOptions object or a number (the base interval in ms).
| 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). |
| 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).
| 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:
| 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.
© 2026, Onur Yıldırım. MIT License.