Skip to content

reactiveTransaction ​

ts
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.

js
await reactiveTransaction(async () => {
  await applyCatch()   // writes the log rows
  await runFollower()  // derives the counts from them
}) // → every screen reading either of them updates once, here

Without 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()
Defersrequeries and notificationsnotifications only
Reads inside itserve the value from before the batchcurrent
May span awaitsno, in practice — everything is frozen meanwhileyes, that is the point
Nestingpassthrough: the inner one is a no-opjoins the outer one
Right forwrites issued together, in one tickan 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() ​

ts
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.

Released under the MIT License.