Why can't we construct a complete JS object in C++ or worker thread so it arrives fully-formed on the main thread with zero work needed?
In Worker Thread:
const parsed = JSON.parse(candidate); // Complete POJSO created here!
batch.push(parsed); // Full object with all nested properties
parentPort!.postMessage({ type: 'data', batch }); // Structured cloningOn Main Thread:
if (msg.type === 'data') {
// msg.batch contains COMPLETE objects - fully constructed!
// No JSON.parse() needed, no object construction needed
this.pending.push(...msg.batch); // Just use them!
}Result: Objects arrive 100% complete via structured cloning. Zero work on main thread!
In C++ Background Thread:
JValue parsed; // C++ struct, not a JS object
parse_json(candidate, parsed); // Parse into C++ data structure
// Can't create N-API values here - need main thread's napi_env!On Main Thread (TSFN callback):
// Must convert C++ struct to JS object HERE (on main thread)
napi_value parsed_val = jvalue_to_js(env, it.value); // Construction happens here!
// This creates the full object structure using N-API callsWhy? N-API values (JS objects) can only be created on the main thread because they need the correct napi_env which is tied to the V8 isolate on the main thread.
Result: Object construction happens on main thread = overhead!
In C++ Background Thread:
// Store raw JSON string
item.raw = candidate;
item.external_data = std::make_unique<uint8_t[]>(candidate.size());
// Pass as Buffer (zero-copy)On Main Thread:
// Receive Buffer, parse to complete object
const parsed = JSON.parse(buf.toString('utf8')); // Complete object created hereResult: Main thread does JSON.parse() but gets complete object. Still some work, but uses V8's optimized parser.
- Parse JSON in background thread → Create complete JS object
- Pass complete object to main thread → Zero work on main thread
- Main thread just receives and uses → Perfect!
| Implementation | Object Construction | Main Thread Work |
|---|---|---|
| Worker | ✅ Complete in worker | ✅ Zero (just receive) |
| Native C++ | ❌ C++ struct → JS on main | |
| Native Buffers |
Worker thread:
- Uses V8's
JSON.parse()→ Creates complete JS object with all properties postMessagewith structured cloning → Transfers complete object graph- Main thread receives → Fully-formed object, zero construction needed
Native C++:
- Parses in C++ → Creates C++ struct
- Must convert to JS on main thread → Construction overhead
- Main thread does work → Not ideal
You're right to be surprised! The worker implementation DOES construct complete objects and passes them via structured cloning. The objects arrive on the main thread fully constructed - no additional work needed!
The native C++ parser has the limitation that N-API values must be created on the main thread, so it can't pass complete objects directly - it has to construct them on the main thread.
- Worker: ✅ Complete objects, zero main thread work
- Native C++:
⚠️ Must construct on main thread - Native Buffers:
⚠️ Must parse on main thread
The worker approach is actually doing exactly what you want - complete objects with zero construction work on the main thread!