Skip to content
JavaScript Basics: Consume Timer Ticks with an Async Iterator

JavaScript Basics: Consume Timer Ticks with an Async Iterator

October 27, 2026

A periodic local task can consume ticks with for await instead of registering a repeating callback. Node’s promise-based interval returns an async iterator. Stop a finite loop explicitly and verify a pre-aborted iterator rejects.

Prepare a scratch directory

Tested on Linux with Node.js v24.19.0. You need the named runtime, Bash, a text editor, and the standard mktemp, rm, and rmdir utilities. The .mjs extension selects ES modules. Assertions check the demonstrated behavior; no npm packages or remote services are needed.

Keep using the same terminal. Stop if setup fails, before saving the source:

start_dir="$PWD"
work_dir="$(mktemp -d)" || exit 1
cd -- "$work_dir" || exit 1

Run the example

Save this as demonstrate-interval-iterator.mjs:

import assert from 'node:assert/strict';
import { setInterval as every } from 'node:timers/promises';

const seen = [];
for await (const value of every(1, 'tick')) {
  seen.push(value);
  console.log(`Tick ${seen.length}: ${value}`);
  if (seen.length === 3) break;
}
assert.deepEqual(seen, ['tick', 'tick', 'tick']);
console.log('Finite interval loop finished');

const controller = new AbortController();
controller.abort();
const cancelled = every(1, 'unused', { signal: controller.signal });
await assert.rejects(cancelled.next(), { name: 'AbortError' });
console.log('Pre-aborted interval: rejected');

Run it:

node demonstrate-interval-iterator.mjs

Observed output:

Tick 1: tick
Tick 2: tick
Tick 3: tick
Finite interval loop finished
Pre-aborted interval: rejected

Read the result

Each iteration receives the value supplied to setInterval. Breaking the loop closes the iterator and clears its interval, allowing this program to finish. The one-millisecond delay is deliberately not measured: scheduling depends on other work. The second case verifies rejection before any tick is consumed, with the rejection handled by assert.rejects.

Apply it carefully

This is an interval schedule, not “wait one delay after the previous job finishes.” When work is slower than the interval, ticks can accumulate and subsequent iterations may become immediately available. Use a timeout after each job when you need a delay between completed jobs. A default referenced interval keeps the process alive, so always provide a termination path. Cancellation is cooperative and cannot undo completed side effects.

The official reference describes the API and its options. The checks above cover the specific fixture, not every possible input or environment.

Clean up

Remove the source and then the empty practice directory:

rm -- demonstrate-interval-iterator.mjs
cd -- "$start_dir" || exit 1
rmdir -- "$work_dir"
unset start_dir work_dir

If the directory is not empty, inspect the remaining files rather than deleting unknown contents.

Acknowledgements

This post was created with assistance from AI.

Last updated on