Batch operations

The Batching API in LiveObjects enables multiple updates to be grouped into a single channel message and applied atomically. It ensures that all operations in a batch either succeed together or are discarded entirely. Batching operations is essential when multiple related updates to channel objects must be applied as a single atomic unit, for example, when application logic depends on multiple objects being updated simultaneously. Without batching, if one operation succeeds while another fails, your application state could become inconsistent.

Note that this differs from Message batching, the native Pub/Sub messages feature. The LiveObjects Batching API is a separate API specifically designed to enable you to group object operations into a single channel message, ensuring that the Ably system guarantees the atomicity of the applied changes.

To batch object operations together, use the channel.objects.batch() method. This method accepts a callback function, which is provided with a batch context object. The batch context object provides a synchronous API to work with objects on a channel that stores operations inside the batch instead of applying them immediately.

Using the batch context ensures that operations are grouped and sent in a single channel message after the batch callback function has run. This guarantees that all changes are applied atomically by both the server and all clients.

JavaScript v2.9
await channel.objects.batch((ctx) => { const root = ctx.getRoot(); root.set('foo', 'bar'); root.set('baz', 42); const counter = root.get('counter'); counter.increment(5); // Batched operations are sent to the Ably system when the batch callback has run. });
Copied!

If an error occurs within the batch, all operations are discarded, preventing partial updates and ensuring atomicity.

The batch context provides a synchronous API for objects operations inside the batch callback. It mirrors the asynchronous API found on channel.objects, including LiveCounter and LiveMap.

To access the batch API, call BatchContext.getRoot(), which synchronously returns a wrapper around the root object instance. This wrapper enables you to access and modify objects within a batch.

JavaScript v2.9
await channel.objects.batch((ctx) => { // Note: .getRoot() call on a batch context is synchronous. // The returned root object is a special wrapper around a regular LiveMap instance, // providing a synchronous mutation API. const root = ctx.getRoot(); // Mutation operations like LiveMap.set and LiveCounter.increment // are synchronous inside the batch and queue operations instead of applying them immediately. root.set('foo', 'bar'); root.remove('baz'); // Access other objects through the root object from the BatchContext.getRoot() method. const counter = root.get('counter'); counter.increment(5); });
Copied!

You cannot create new objects using the batch context. If you need to create new objects and add them to the channel as part of an atomic batch operation to guarantee atomicity, you must first create them using the regular channel.objects API. Once the objects have been created, you can then assign them to the object tree inside a batch function.

JavaScript v2.9
// First, create new objects outside the batch context const counter = await channel.objects.createCounter(); const map = await channel.objects.createMap(); // Then, use a batch to assign them atomically to the channel objects await channel.objects.batch((ctx) => { const root = ctx.getRoot(); root.set('counter', counter); root.set('map', map); });
Copied!

Usually, you don’t need to use batching for objects operations. It is only useful in situations where a group of operations must be applied together to maintain consistency in application state, or when there are multiple mutation operations that you might want to apply at the same time to improve the UI experience.

For example, in a task dashboard application, you might want to remove all tasks on a board in a single operation to prevent excessive UI updates that the user would otherwise experience.

JavaScript v2.9
await channel.objects.batch((ctx) => { const root = ctx.getRoot(); const tasks = root.get('tasks'); for (const key of reactions.keys()) { reactions.remove(key); } });
Copied!

To explicitly cancel a batch before it is applied, throw an error inside the batch function. This prevents any queued operations from being applied.

JavaScript v2.9
await channel.objects.batch((ctx) => { const root = ctx.getRoot(); root.set('foo', 'bar'); // Throwing an error prevents any queued operations from being applied. throw new Error('Cancel batch'); });
Copied!

The Batch API provided by the batch context object cannot be used outside the callback function. Attempting to do so results in an error. This applies both to BatchContext.getRoot() and any object instances retrieved from it.

JavaScript v2.9
let root; await channel.objects.batch((ctx) => { root = ctx.getRoot(); }); // Calling any Batch API methods outside the batch callback // will throw an Error: Batch is closed. root.set('foo', 'bar');
Copied!
Select...