batch
Groups multiple signal updates to trigger effects only once.
API
function batch<T>(fn: () => T): T
fn
: A function that contains the signal updates to be batched.- Returns: The value returned by the function
fn
.
TypeScript
The batch
function is generic and preserves the return type of the function it wraps.
type BatchFunction<T> = () => T;
const result: string = batch(() => {
// ... signal updates
return "done";
});
Basic Usage
Without batch
, each signal update triggers its own effect run. With batch
, all updates are grouped, and effects run only once.
import { signal, batch, effect } from '@hellajs/core';
const firstName = signal("John");
const lastName = signal("Doe");
let updateCount = 0;
effect(() => {
console.log(`${firstName()} ${lastName()}`);
updateCount++;
});
// Initial run: logs "John Doe", updateCount is 1
// Without batching: 2 separate effect runs
firstName("Jane"); // Logs "Jane Doe"
lastName("Smith"); // Logs "Jane Smith"
console.log(updateCount); // 3
// With batching: 1 effect run for both updates
batch(() => {
firstName("Alice");
lastName("Johnson");
}); // Logs "Alice Johnson"
console.log(updateCount); // 4 (only 1 additional update)
Key Concepts
Update Deferral
Batch defers all reactive updates until the batch function completes, preventing intermediate state inconsistencies and reducing unnecessary computations.
const a = signal(1);
const b = signal(2);
effect(() => console.log(a() + b()));
batch(() => {
a(10); // Effect doesn't run yet
b(20); // Effect doesn't run yet
}); // Effect runs once here: logs 30
Synchronous Boundaries
Batching only applies to synchronous signal updates within the batch function - async operations break batch boundaries.
batch(() => {
signal1('value1'); // Batched
setTimeout(() => signal2('value2'), 0); // Not batched
signal3('value3'); // Batched with signal1
});
Performance Optimization
Reduces the number of effect executions and DOM updates when multiple related signals change simultaneously.
// Without batch: 3 effect runs
name('John'); // Effect runs
age(30); // Effect runs
email('john@example.com'); // Effect runs
// With batch: 1 effect run
batch(() => {
name('John');
age(30);
email('john@example.com');
}); // Effect runs once
Nested Batch Behavior
Nested batches are flattened - effects only run once the outermost batch completes, regardless of nesting depth.
batch(() => {
signal1('a');
batch(() => {
signal2('b'); // Still part of outer batch
});
signal3('c');
}); // All effects run together at the end
Important Considerations
Async Operations
Batching only applies to synchronous signal updates within the batch function. Asynchronous operations break batch boundaries.
// ❌ Async operations break batching
batch(() => {
signal1('value');
setTimeout(() => signal2('delayed'), 0);
});
// ✅ Batch synchronous operations separately
batch(() => { signal1('value'); signal2('value'); });
Nested Batching
Batches can be nested, but effects only run when the outermost batch completes.
// ✅ Nested batches are flattened
batch(() => {
signal1('a');
batch(() => signal2('b'));
signal3('c');
}); // Effects run once at the end