REFERENCE / CORE API

flushSync()

Runs every pending effect now — including every DOM binding — instead of waiting for the microtask in which they would otherwise run. Also drains any onMount callbacks the flush produced.

Signature

TS
flushSync(): void

Why it exists

A write updates a signal's value immediately but defers its effects. Almost always that is what you want: many writes, one DOM pass. The exception is code that needs to read the DOM after a write in the same task — a measurement, a scroll position, a test assertion.

JS
count.value = 5;
el.textContent;    // still the old number — the DOM effect is queued, not run

flushSync();
el.textContent;    // "5"

The FLIP pattern

The textbook use: measure, mutate, flush, measure again, then animate the difference. The runtime does no animation of its own, so this is plain Web Animations on real nodes.

JS
// measure → mutate → flushSync → measure again, in one synchronous frame
const before = row.getBoundingClientRect();
items.value = reordered;
flushSync();                       // the rows have moved now
const after = row.getBoundingClientRect();
row.animate([{ transform: `translateY(${before.top - after.top}px)` }, { transform: 'none' }], 200);

flushSync vs tick

flushSync() is synchronous and forces the work. await tick() simply waits for the flush that was going to happen anyway — the right choice in async code, and the one that cannot run effects re-entrantly.

Try it

src/islands/scheduling.ts
import { signal, flushSync, tick, html, mount } from '@amojs.dev/core';

const n = signal(0);
const report = signal('click a button');
let span: HTMLElement;

const withFlushSync = () => {
  n.value++;
  const before = span.textContent; // the value changed, the DOM has not — effects wait for a microtask
  flushSync(); // run every pending effect now
  report.value = `before flushSync the DOM showed ${before}, after it shows ${span.textContent}`;
};

const withTick = async () => {
  n.value++;
  const before = span.textContent;
  await tick(); // resolves once the pending effects have run
  report.value = `before tick the DOM showed ${before}, after it shows ${span.textContent}`;
};

const Demo = () => html`<div class="demo">
  <p>n = <b ref=${(el: HTMLElement) => (span = el)}>${n}</b></p>
  <div class="row">
    <button class="counter" onclick=${withFlushSync}>n++ then flushSync()</button>
    <button class="counter" onclick=${withTick}>n++ then await tick()</button>
  </div>
  <p class="status">${report}</p>
</div>`;

const host = document.getElementById('demo');
if (host) mount(Demo, host);
live preview● running

See also