reactiveTransaction
import { reactiveTransaction } from '@signaldb/core'Runs a callback as one reactive transaction: every reactive scope that would have been notified while it runs is notified once, after it ends.
await reactiveTransaction(async () => {
await applyCatch() // writes the log rows
await runFollower() // derives the counts from them
}) // → every screen reading either of them updates once, hereWithout it, an operation made of several phases wakes its readers once per phase. The user did one thing; the screen re-rendered three times.
How it differs from batch
Collection.batch() defers the query pipeline: while it is open, no cursor requeries at all. That makes it right for a handful of writes issued together, and wrong for anything longer — it has to stay synchronous and short, because everything reading a collection is frozen for its duration.
reactiveTransaction holds only the last step, the notification of reactive scopes. Queries keep running, results stay current, observers keep diffing; what waits is the wake-up.
Collection.batch() | reactiveTransaction() | |
|---|---|---|
| Defers | requeries and notifications | notifications only |
| Reads inside it | serve the value from before the batch | current |
May span awaits | no, in practice — everything is frozen meanwhile | yes, that is the point |
| Nesting | passthrough: the inner one is a no-op | joins the outer one |
| Right for | writes issued together, in one tick | an operation with phases |
They compose: a transaction may contain several batches, which is what a real pipeline looks like.
Behaviour
- Nesting joins. An inner transaction does not flush; the outermost one does. A pipeline can open one without knowing whether its caller already did.
- Overlap holds. While any transaction is open, notifications are held, and they are flushed when the last one ends. A scope may be woken later than its own transaction ended, never earlier.
- A throw still flushes. Whatever was written before the failure is real, so its readers are notified. A transaction that swallowed its notifications on failure would leave a screen showing state that is no longer in the database.
- Once per scope. A transaction that touched one query fifty times wakes it once.
- A disposed query is not woken. A cursor cleaned up while a transaction is open is dropped from it.
- Synchronous in, synchronous out. A synchronous callback returns its value directly and flushes before returning.
isInReactiveTransaction()
import { isInReactiveTransaction } from '@signaldb/core'Returns whether a reactive transaction is currently open. Useful for diagnostics and for code that wants to behave differently while notifications are held.
WARNING
A transaction that never settles holds notifications for the rest of the process: nothing reactive updates again. Keep the callback's promise on a path that always resolves or rejects.