Core Concepts
Batching changes
Group multiple model updates and emit one final expression change.
Practical value
Key points
- One completed cycle usually produces one final changed event per root expression.
- Back-to-back writes in the same synchronous block can already collapse into one emission, even without suspend().
- Use suspend() and continue() when updates happen over multiple async steps but should still notify once.
- Use normal updates when intermediate values are meaningful for your flow.
- changed is only emitted when the final value is actually different.
What this example proves
The example intentionally flushes between unbatched writes, so each write gets its own commit and you can see multiple emissions.
In the batched part, the same writes run while suspended. continue() then flushes once, so subscribers see one final emission.
Why unbatched can still look like one emission
If several writes happen immediately after each other in one synchronous turn, rs-x can still publish one final value.
That is expected behavior. It means the system coalesced those writes before the commit boundary.
When to use suspend and continue
Use it for multi-step updates where partial states should stay private until everything is ready.
Typical cases are import pipelines, validation-and-fix passes, or grouped recalculations where you only care about the final result.
Example
import { emptyFunction, InjectionContainer, WaitForEvent } from '@rs-x/core';
import {
type IExpressionChangeTransactionManager,
rsx,
RsXExpressionParserInjectionTokens,
RsXExpressionParserModule,
} from '@rs-x/expression-parser';
await InjectionContainer.load(RsXExpressionParserModule);
const tx = InjectionContainer.get<IExpressionChangeTransactionManager>(
RsXExpressionParserInjectionTokens.IExpressionChangeTransactionManager,
);
const model = {
subtotal: 100,
shipping: 15,
discount: 5,
};
const totalExpression = rsx<number>('subtotal + shipping - discount')(model);