batch()#
import { batch } from 'janux';
batch(() => {
price.value = 300;
qty.value = 4;
}); // subscribers run once, not twiceGroups writes so every affected effect runs once, after the function returns. batch<T>(fn: () => T): T — returns whatever fn returns.
import { signal, computed, watch, batch } from 'janux';
const price = signal(250);
const qty = signal(2);
let runs = 0;
watch(() => {
price.value;
qty.value;
runs += 1;
});
// runs === 1 (initial)
batch(() => {
price.value = 300;
qty.value = 4;
});
// runs === 2, not 3 — one notification for both writesNesting is transparent#
An inner batch joins the outer one: notifications flush when the outermost batch exits. That makes it safe to batch inside a helper without knowing your caller:
function applyDiscount() {
batch(() => { price.value *= 0.9; }); // joins any enclosing batch
}
batch(() => {
applyDiscount();
qty.value = 10;
}); // still a single flushReads inside a batch see writes immediately#
Batching defers notifications, not assignments — price.value inside the batch already returns the new value. Only subscriber re-runs wait.
Intents batch for you#
Every mutation inside an intent's run() is already applied as one atomic update: the view repaints once, the audit trail records one entry, and agents see one coherent state transition. Reach for batch when you're mutating raw signals outside a component.
Related: signal · watch · Intents and guards