Skip to content

Workers

A worker keeps large synchronous spreadsheet operations off the main thread. Each worker has its own WebAssembly instance and configuration. Set locale, currency, codepage tables, and any other configuration inside the worker; it does not inherit those settings from the main thread.

Copy dist/xlsx.full.min.js to your web server. Create these two files next to it:

worker.js
importScripts("xlsx.full.min.js");
self.onmessage = (event) => {
try {
const wb = XLSX.read(event.data, { type: "array" });
const ws = wb.Sheets[wb.SheetNames[0]];
self.postMessage({ rows: XLSX.utils.sheet_to_json(ws, { header: 1 }) });
} catch (error) {
self.postMessage({ error: String(error) });
}
};
index.html
<input type="file" id="file">
<script>
const worker = new Worker("worker.js");
worker.onmessage = (event) => console.log(event.data);
document.getElementById("file").addEventListener("change", async (event) => {
const file = event.target.files[0];
if (!file) return;
const buffer = await file.arrayBuffer();
worker.postMessage(buffer, [buffer]);
});
</script>

The transfer list moves ownership of the buffer to the worker. The main thread cannot use it afterwards. CSP requirements also apply to workers.

This self-contained example starts a worker, parses CSV, and sends one value back:

const { Worker } = require("node:worker_threads");
const assert = require("node:assert/strict");
const worker = new Worker(`
const { parentPort, workerData } = require("node:worker_threads");
const XLSX = require(workerData.packagePath);
const wb = XLSX.read("Item,Count\\nBolt,3", { type: "string" });
parentPort.postMessage(wb.Sheets.Sheet1.B2.v);
`, { eval: true, workerData: { packagePath: require.resolve("@agent-sheet/wasm") } });
worker.once("message", (value) => assert.equal(value, 3));
worker.once("error", (error) => { throw error; });

End a long-lived worker with worker.terminate() when you no longer need it. Its memory is separate from the main thread’s instance.