Chapter 01 @dirtytalk/engine

Mark. Coalesce. Flush.

Everything in dirtytalk runs on one small loop: a channel collects what changed, a scheduler picks when, and only the subscribers who care get told. This stage runs the real @dirtytalk/engine.

01

Something changed.

A mutation calls channel.mark(region). Here a region is a set of keys. With SyncScheduler the channel flushes right away, and the Avatar, which cares about users, wakes up.

channel.mark(new Set(['users']));

02

Marks coalesce.

Six marks land in one task. With MicrotaskScheduler they union into a single dirty region, and the channel flushes once at the end of the task. Watch the timeline: six marks below, one flush above.

03

Only who cares.

Every subscriber declares an interest region. On flush, the channel intersects it with the dirty region and skips the rest. Nobody listens to route, so marking it wakes no one. theme wakes only the toggle.

channel.subscribe(
  () => new Set(['theme']),
  (dirty) => repaintToggle(),
);

04

You choose when.

Real marks arrive from many tasks: clicks, timers, sockets. A microtask flush runs at the end of each task, so a storm of separate tasks still flushes once per mark. Switch to RAFScheduler and everything that lands before the next frame flushes together.

05

Or hold the flush.

ManualScheduler waits for you. Marks pile up as pending keys in the channel until .pump() flushes them all at once. It's handy in tests and in your own frame loop. The hero on the home page pumps its scene this way.

06

Your turn.

Click a key to mark it, switch schedulers, set off a burst or a storm, and watch the counts underneath: marks in, flushes out, wakes only where interest meets dirt.

channel.mark()

DirtyChannel

  • users
  • theme
  • cart
  • route

subscribe()

Avatar users 0 wakes
Theme toggle theme 0 wakes
Cart badge cartusers 0 wakes
flush mark

0 marks → 0 flushes → 0 wakes