0
# Transferable Objects
1
2
Efficient data transfer mechanisms using structured cloning and transferable objects for optimal performance with large data sets.
3
4
## Capabilities
5
6
### Piscina.move Function
7
8
Primary utility for marking objects as transferable to optimize data transfer between main thread and workers.
9
10
```typescript { .api }
11
/**
12
* Mark objects for efficient transfer to worker threads
13
* Transfers ownership instead of copying data
14
* @param val - Object to transfer (ArrayBuffer, MessagePort, etc.)
15
* @returns Wrapped transferable object
16
*/
17
function move(
18
val: Transferable | TransferListItem | ArrayBufferView | ArrayBuffer | MessagePort
19
): any;
20
```
21
22
**Usage Examples:**
23
24
```typescript
25
import Piscina from "piscina";
26
27
const pool = new Piscina({ filename: "worker.js" });
28
29
// Transfer ArrayBuffer efficiently
30
const buffer = new ArrayBuffer(1024 * 1024); // 1MB buffer
31
const transferableBuffer = Piscina.move(buffer);
32
33
await pool.run({
34
data: transferableBuffer,
35
operation: "processLargeData"
36
});
37
38
// buffer is now unusable in main thread (ownership transferred)
39
console.log(buffer.byteLength); // 0 - buffer is detached
40
41
// Transfer typed arrays
42
const uint8Array = new Uint8Array(1000);
43
uint8Array.fill(42);
44
45
await pool.run({
46
data: Piscina.move(uint8Array), // Transfers underlying ArrayBuffer
47
operation: "processTypedArray"
48
});
49
50
// Transfer multiple objects
51
const buffer1 = new ArrayBuffer(512);
52
const buffer2 = new ArrayBuffer(256);
53
54
await pool.run({
55
buffers: [Piscina.move(buffer1), Piscina.move(buffer2)],
56
operation: "processMultipleBuffers"
57
});
58
```
59
60
### Transferable Interface
61
62
Interface for objects that can be efficiently transferred between threads.
63
64
```typescript { .api }
65
/**
66
* Interface for transferable objects
67
*/
68
interface Transferable {
69
/** Internal transferable object reference */
70
readonly [transferableSymbol]: object;
71
72
/** Internal value object reference */
73
readonly [valueSymbol]: object;
74
}
75
```
76
77
### Transfer List Types
78
79
Type definitions for transferable object collections.
80
81
```typescript { .api }
82
/**
83
* Array of transferable objects
84
* Extracted from MessagePort.postMessage signature
85
*/
86
type TransferList = MessagePort extends {
87
postMessage: (value: any, transferList: infer T) => any;
88
} ? T : never;
89
90
/**
91
* Individual transferable object type
92
*/
93
type TransferListItem = TransferList extends Array<infer T> ? T : never;
94
```
95
96
### Manual Transfer Lists
97
98
Use transfer lists directly in run options for fine-grained control.
99
100
```typescript { .api }
101
interface RunOptions {
102
/** Objects to transfer ownership to worker (for performance) */
103
transferList?: TransferList;
104
}
105
```
106
107
**Usage Examples:**
108
109
```typescript
110
import Piscina from "piscina";
111
112
const pool = new Piscina({ filename: "worker.js" });
113
114
// Manual transfer list (without Piscina.move)
115
const buffer = new ArrayBuffer(2048);
116
const result = await pool.run(
117
{ data: buffer, operation: "process" },
118
{ transferList: [buffer] }
119
);
120
121
// Mixed approach: some moved, some in transfer list
122
const buffer1 = new ArrayBuffer(1024);
123
const buffer2 = new ArrayBuffer(1024);
124
const port = new MessageChannel().port1;
125
126
await pool.run(
127
{
128
moved: Piscina.move(buffer1), // Automatic transfer
129
manual: buffer2, // Manual transfer
130
port: port // Manual transfer
131
},
132
{ transferList: [buffer2, port] } // Explicit transfer list
133
);
134
```
135
136
### Transferable Object Detection
137
138
Utilities for working with transferable objects.
139
140
```typescript { .api }
141
/**
142
* Check if object implements Transferable interface
143
* @param value - Object to check
144
* @returns True if object is transferable
145
*/
146
function isTransferable(value: unknown): boolean;
147
148
/**
149
* Check if object is marked as movable by Piscina.move()
150
* @param value - Object to check
151
* @returns True if object was processed by move()
152
*/
153
function isMovable(value: any): boolean;
154
```
155
156
**Usage Examples:**
157
158
```typescript
159
import { isTransferable, isMovable, move } from "piscina";
160
161
const buffer = new ArrayBuffer(1024);
162
const movedBuffer = move(buffer);
163
const regularObject = { data: "test" };
164
165
console.log(isTransferable(buffer)); // false (not wrapped)
166
console.log(isTransferable(movedBuffer)); // true (wrapped by move)
167
console.log(isMovable(movedBuffer)); // true (marked as movable)
168
console.log(isTransferable(regularObject)); // false
169
console.log(isMovable(regularObject)); // false
170
171
// Use in conditional logic
172
function processData(data: any) {
173
if (isMovable(data)) {
174
console.log("Data will be transferred efficiently");
175
} else {
176
console.log("Data will be cloned (slower for large objects)");
177
}
178
}
179
```
180
181
### Supported Transferable Types
182
183
Objects that can be transferred efficiently between threads:
184
185
```typescript { .api }
186
// Native transferable types (partial list)
187
type NativeTransferableTypes =
188
| ArrayBuffer
189
| MessagePort
190
| ReadableStream
191
| WritableStream
192
| TransformStream
193
| AudioData
194
| ImageBitmap
195
| OffscreenCanvas;
196
197
// Typed array views (transfer underlying ArrayBuffer)
198
type TypedArrayTypes =
199
| Int8Array
200
| Uint8Array
201
| Uint8ClampedArray
202
| Int16Array
203
| Uint16Array
204
| Int32Array
205
| Uint32Array
206
| Float32Array
207
| Float64Array
208
| BigInt64Array
209
| BigUint64Array;
210
```
211
212
**Usage Examples:**
213
214
```typescript
215
import Piscina from "piscina";
216
217
const pool = new Piscina({ filename: "worker.js" });
218
219
// ArrayBuffer transfer
220
const arrayBuffer = new ArrayBuffer(1024);
221
await pool.run({ buffer: Piscina.move(arrayBuffer) });
222
223
// Typed array transfer (transfers underlying ArrayBuffer)
224
const float32Array = new Float32Array(256);
225
await pool.run({ floats: Piscina.move(float32Array) });
226
227
// MessagePort transfer
228
const { port1, port2 } = new MessageChannel();
229
await pool.run({
230
port: Piscina.move(port1),
231
setupCommunication: true
232
});
233
234
// Multiple transfers
235
const data = {
236
buffer1: Piscina.move(new ArrayBuffer(512)),
237
buffer2: Piscina.move(new ArrayBuffer(256)),
238
array: Piscina.move(new Uint8Array(128))
239
};
240
await pool.run(data);
241
```
242
243
### Performance Considerations
244
245
#### When to Use Transferable Objects
246
247
```typescript
248
import Piscina from "piscina";
249
250
const pool = new Piscina({ filename: "worker.js" });
251
252
// ✅ Good: Large data structures
253
const largeBuffer = new ArrayBuffer(10 * 1024 * 1024); // 10MB
254
await pool.run({
255
data: Piscina.move(largeBuffer), // Efficient transfer
256
operation: "processLargeData"
257
});
258
259
// ✅ Good: Frequent transfers of medium-sized data
260
const imageData = new Uint8Array(1920 * 1080 * 4); // 8MB image
261
await pool.run({
262
pixels: Piscina.move(imageData),
263
operation: "applyFilter"
264
});
265
266
// ❌ Avoid: Small objects (overhead not worth it)
267
const smallArray = new Uint8Array(10);
268
await pool.run({
269
data: smallArray, // Just clone, don't transfer
270
operation: "processSmall"
271
});
272
273
// ❌ Avoid: Objects you need to keep using
274
const buffer = new ArrayBuffer(1024);
275
// Don't move if you need buffer later
276
await pool.run({
277
data: { ...buffer }, // Clone instead
278
operation: "process"
279
});
280
// buffer is still usable
281
```
282
283
#### Transfer vs Clone Performance
284
285
```typescript
286
import Piscina from "piscina";
287
288
const pool = new Piscina({ filename: "worker.js" });
289
290
async function performanceComparison() {
291
const largeBuffer = new ArrayBuffer(50 * 1024 * 1024); // 50MB
292
293
// Measure cloning performance
294
console.time('Clone Transfer');
295
await pool.run({
296
data: largeBuffer, // Will be cloned
297
operation: "benchmark"
298
});
299
console.timeEnd('Clone Transfer');
300
301
// Measure transfer performance
302
const anotherBuffer = new ArrayBuffer(50 * 1024 * 1024);
303
console.time('Move Transfer');
304
await pool.run({
305
data: Piscina.move(anotherBuffer), // Will be transferred
306
operation: "benchmark"
307
});
308
console.timeEnd('Move Transfer'); // Should be much faster
309
}
310
```
311
312
### Worker-Side Handling
313
314
How workers receive and process transferred objects:
315
316
**Worker file (worker.js):**
317
318
```javascript
319
// Worker receives transferred objects normally
320
module.exports = function(data) {
321
// data.buffer is now owned by this worker thread
322
const { buffer, operation } = data;
323
324
if (operation === "processLargeData") {
325
// Work with transferred ArrayBuffer
326
const view = new Uint8Array(buffer);
327
328
// Process data...
329
for (let i = 0; i < view.length; i++) {
330
view[i] = view[i] * 2;
331
}
332
333
// Can transfer back to main thread
334
return {
335
result: "processed",
336
// Transfer modified buffer back
337
processedData: buffer // Will be transferred back
338
};
339
}
340
341
return { result: "complete" };
342
};
343
```
344
345
### Symbols and Constants
346
347
Access transferable-related symbols for advanced usage.
348
349
```typescript { .api }
350
class Piscina {
351
/** Symbol used to mark transferable objects */
352
static readonly transferableSymbol: symbol;
353
354
/** Symbol used to access transferable values */
355
static readonly valueSymbol: symbol;
356
357
/** Symbol used for queue options */
358
static readonly queueOptionsSymbol: symbol;
359
}
360
361
// Named exports for convenience
362
const transferableSymbol: typeof Piscina.transferableSymbol;
363
const valueSymbol: typeof Piscina.valueSymbol;
364
const queueOptionsSymbol: typeof Piscina.queueOptionsSymbol;
365
```
366
367
**Usage Examples:**
368
369
```typescript
370
import { transferableSymbol, valueSymbol } from "piscina";
371
372
// Create custom transferable wrapper
373
class CustomTransferable {
374
constructor(private data: ArrayBuffer) {}
375
376
get [transferableSymbol]() {
377
return this.data;
378
}
379
380
get [valueSymbol]() {
381
return this.data;
382
}
383
}
384
385
// Use custom transferable
386
const customData = new CustomTransferable(new ArrayBuffer(1024));
387
await pool.run({ data: customData });
388
```
389
390
### Best Practices
391
392
#### Memory Management
393
394
```typescript
395
import Piscina from "piscina";
396
397
const pool = new Piscina({ filename: "worker.js" });
398
399
// ✅ Good: Clear transfer intent
400
async function processLargeFile(fileBuffer: ArrayBuffer) {
401
try {
402
const result = await pool.run({
403
data: Piscina.move(fileBuffer),
404
operation: "parseFile"
405
});
406
407
// fileBuffer is now detached, don't use it
408
return result;
409
} catch (error) {
410
// Handle errors - buffer may still be detached
411
console.error("Processing failed:", error);
412
throw error;
413
}
414
}
415
416
// ✅ Good: Keep reference if needed later
417
async function processWithBackup(originalBuffer: ArrayBuffer) {
418
// Clone for transfer, keep original
419
const bufferCopy = originalBuffer.slice();
420
421
const result = await pool.run({
422
data: Piscina.move(bufferCopy),
423
operation: "process"
424
});
425
426
// originalBuffer is still usable
427
return { result, originalSize: originalBuffer.byteLength };
428
}
429
```
430
431
#### Error Handling
432
433
```typescript
434
import Piscina from "piscina";
435
436
const pool = new Piscina({ filename: "worker.js" });
437
438
async function safeTransfer(buffer: ArrayBuffer) {
439
// Check if buffer is already detached
440
if (buffer.byteLength === 0) {
441
throw new Error("Buffer is already detached");
442
}
443
444
try {
445
const result = await pool.run({
446
data: Piscina.move(buffer),
447
operation: "process"
448
});
449
return result;
450
} catch (error) {
451
// Buffer is detached even if task failed
452
console.warn("Task failed, buffer is detached");
453
throw error;
454
}
455
}
456
```