0
# Transaction System
1
2
Atomic change batching for consistent updates and coordinated event handling. The transaction system ensures that multiple operations are executed atomically and that all observers are notified consistently.
3
4
## Capabilities
5
6
### Transaction Class
7
8
Represents a single atomic change context that groups multiple operations together.
9
10
```typescript { .api }
11
/**
12
* Transaction context for atomic operations
13
*/
14
class Transaction {
15
/** Document this transaction operates on */
16
readonly doc: Doc;
17
18
/** Origin identifier for this transaction */
19
readonly origin: any;
20
21
/** Whether this transaction originated locally */
22
readonly local: boolean;
23
24
/** Set of items deleted in this transaction */
25
readonly deleteSet: DeleteSet;
26
27
/** Document state before transaction began */
28
readonly beforeState: Map<number, number>;
29
30
/** Document state after transaction completes */
31
readonly afterState: Map<number, number>;
32
33
/** Map of changed types to their changed keys/indices */
34
readonly changed: Map<AbstractType<YEvent<any>>, Set<string | null>>;
35
36
/** Map of parent types that will emit events */
37
readonly changedParentTypes: Map<AbstractType<YEvent<any>>, Array<YEvent<any>>>;
38
39
/** Transaction metadata */
40
readonly meta: Map<any, any>;
41
42
/** Subdocuments added during transaction */
43
readonly subdocsAdded: Set<Doc>;
44
45
/** Subdocuments removed during transaction */
46
readonly subdocsRemoved: Set<Doc>;
47
48
/** Subdocuments loaded during transaction */
49
readonly subdocsLoaded: Set<Doc>;
50
}
51
```
52
53
### Transaction Function
54
55
Execute operations within a transaction context.
56
57
```typescript { .api }
58
/**
59
* Execute a function within a transaction context
60
* @param doc - Document to operate on
61
* @param f - Function to execute within the transaction
62
* @param origin - Optional origin identifier for the transaction
63
* @returns Result of the function execution
64
*/
65
function transact<T>(doc: Doc, f: (transaction: Transaction) => T, origin?: any): T;
66
```
67
68
**Usage Examples:**
69
70
```typescript
71
import * as Y from "yjs";
72
73
const doc = new Y.Doc();
74
const yarray = doc.getArray("items");
75
const ymap = doc.getMap("metadata");
76
77
// Basic transaction
78
Y.transact(doc, (transaction) => {
79
yarray.push(["item1", "item2"]);
80
ymap.set("count", yarray.length);
81
82
// Access transaction properties
83
console.log("Transaction origin:", transaction.origin);
84
console.log("Changed types:", transaction.changed.size);
85
});
86
87
// Transaction with origin
88
Y.transact(doc, () => {
89
yarray.delete(0, 1);
90
}, "user-delete");
91
92
// Transaction returning a value
93
const result = Y.transact(doc, () => {
94
yarray.push(["new-item"]);
95
return yarray.length;
96
}, "add-item");
97
```
98
99
### Document Transaction Method
100
101
The Doc class also provides a transaction method for convenience.
102
103
```typescript { .api }
104
/**
105
* Execute a function within a transaction on this document
106
* @param f - Function to execute within the transaction
107
* @param origin - Optional origin identifier for the transaction
108
* @returns Result of the function execution
109
*/
110
transact<T>(f: (transaction: Transaction) => T, origin?: any): T;
111
```
112
113
**Usage Examples:**
114
115
```typescript
116
import * as Y from "yjs";
117
118
const doc = new Y.Doc();
119
const yarray = doc.getArray("items");
120
121
// Using doc.transact method
122
doc.transact(() => {
123
yarray.push(["item1", "item2", "item3"]);
124
}, "batch-insert");
125
126
// Accessing transaction in observers
127
yarray.observe((event, transaction) => {
128
console.log("Array changed in transaction:", transaction.origin);
129
console.log("Local change:", transaction.local);
130
});
131
```
132
133
### Transaction Lifecycle Events
134
135
Documents emit events during transaction lifecycle for advanced use cases.
136
137
```typescript { .api }
138
/**
139
* Transaction lifecycle events
140
*/
141
interface TransactionEvents {
142
/** Fired before transaction begins */
143
beforeTransaction: (transaction: Transaction, doc: Doc) => void;
144
145
/** Fired after transaction completes but before observers */
146
afterTransaction: (transaction: Transaction, doc: Doc) => void;
147
148
/** Fired before observer functions are called */
149
beforeObserverCalls: (transaction: Transaction, doc: Doc) => void;
150
151
/** Fired after all observer functions complete */
152
afterObserverCalls: (transaction: Transaction, doc: Doc) => void;
153
}
154
```
155
156
**Usage Examples:**
157
158
```typescript
159
import * as Y from "yjs";
160
161
const doc = new Y.Doc();
162
163
// Listen to transaction lifecycle
164
doc.on("beforeTransaction", (transaction) => {
165
console.log("Transaction starting:", transaction.origin);
166
transaction.meta.set("startTime", Date.now());
167
});
168
169
doc.on("afterTransaction", (transaction) => {
170
const duration = Date.now() - transaction.meta.get("startTime");
171
console.log(`Transaction completed in ${duration}ms`);
172
});
173
174
doc.on("beforeObserverCalls", (transaction) => {
175
console.log("About to call observers for:", Array.from(transaction.changed.keys()));
176
});
177
178
doc.on("afterObserverCalls", (transaction) => {
179
console.log("All observers completed");
180
});
181
```
182
183
### Advanced Transaction Patterns
184
185
**Conditional Operations:**
186
187
```typescript
188
import * as Y from "yjs";
189
190
const doc = new Y.Doc();
191
const yarray = doc.getArray("items");
192
193
// Conditional operations within transaction
194
doc.transact((transaction) => {
195
if (yarray.length === 0) {
196
yarray.push(["initial-item"]);
197
}
198
199
// Check if this is a local change
200
if (transaction.local) {
201
yarray.push(["local-change-marker"]);
202
}
203
}, "conditional-update");
204
```
205
206
**Cross-Type Operations:**
207
208
```typescript
209
import * as Y from "yjs";
210
211
const doc = new Y.Doc();
212
const tasks = doc.getArray("tasks");
213
const metadata = doc.getMap("metadata");
214
215
// Coordinate changes across multiple types
216
doc.transact(() => {
217
tasks.push([{ id: 1, title: "New Task", completed: false }]);
218
metadata.set("lastTaskId", 1);
219
metadata.set("taskCount", tasks.length);
220
metadata.set("lastModified", new Date().toISOString());
221
}, "add-task");
222
```
223
224
**Transaction Metadata:**
225
226
```typescript
227
import * as Y from "yjs";
228
229
const doc = new Y.Doc();
230
const ytext = doc.getText("document");
231
232
// Using transaction metadata
233
doc.on("beforeTransaction", (transaction) => {
234
transaction.meta.set("user", "alice");
235
transaction.meta.set("timestamp", Date.now());
236
});
237
238
ytext.observe((event, transaction) => {
239
const user = transaction.meta.get("user");
240
const timestamp = transaction.meta.get("timestamp");
241
console.log(`Text changed by ${user} at ${timestamp}`);
242
});
243
244
doc.transact(() => {
245
ytext.insert(0, "Hello World!");
246
}, "text-insert");
247
```
248
249
**Nested Transactions:**
250
251
```typescript
252
import * as Y from "yjs";
253
254
const doc = new Y.Doc();
255
const yarray = doc.getArray("items");
256
257
// Note: Nested transact calls are flattened into single transaction
258
doc.transact(() => {
259
yarray.push(["item1"]);
260
261
// This doesn't create a nested transaction - operations are merged
262
doc.transact(() => {
263
yarray.push(["item2"]);
264
});
265
266
yarray.push(["item3"]);
267
}, "batch-operation");
268
269
// All three push operations happen in single transaction
270
```
271
272
### Transaction Performance Considerations
273
274
**Batching Operations:**
275
276
```typescript
277
import * as Y from "yjs";
278
279
const doc = new Y.Doc();
280
const yarray = doc.getArray("items");
281
282
// Inefficient: Multiple separate transactions
283
for (let i = 0; i < 1000; i++) {
284
yarray.push([`item-${i}`]);
285
}
286
287
// Efficient: Single transaction for bulk operations
288
doc.transact(() => {
289
for (let i = 0; i < 1000; i++) {
290
yarray.push([`item-${i}`]);
291
}
292
}, "bulk-insert");
293
```
294
295
**Observer Optimization:**
296
297
```typescript
298
import * as Y from "yjs";
299
300
const doc = new Y.Doc();
301
const yarray = doc.getArray("items");
302
303
yarray.observe((event) => {
304
// This fires once per transaction, not once per operation
305
console.log("Array changed, operations:", event.changes.delta.length);
306
});
307
308
doc.transact(() => {
309
yarray.push(["item1"]);
310
yarray.push(["item2"]);
311
yarray.push(["item3"]);
312
// Observer fires once with all changes
313
});
314
```