Skip to content

Threads

Heavy calculations on the main thread freeze the page: no scrolling, no clicks, no animations until they finish. Thread moves that work to a background thread (a Web Worker) with a small class-based API, in the spirit of Java’s Thread.

Extend Thread and override two methods:

  • run(params) does the work. It runs inside the worker.
  • onMessage(event) receives the results. It runs on the main thread.
src/PrimeThread.js
import { Thread, EventBus } from "jops-core";
export default class PrimeThread extends Thread {
run(params) {
// runs in a Web Worker: only params and worker globals are available
let count = 0;
for (let n = 2; n < params.limit; n++) {
let prime = true;
for (let d = 2; d * d <= n; d++) {
if (n % d === 0) { prime = false; break; }
}
if (prime) count++;
}
self.postMessage(count);
}
onMessage(event) {
// runs on the main thread
EventBus.send("primes:done", event.data);
}
}

Start it with parameters, and stop it when it is no longer needed:

const thread = new PrimeThread();
thread.start({ limit: 1_000_000 });
// later, for example in a view's destroy()
thread.terminate();

start(params?) spawns the worker, runs run(params) in it and wires onMessage() for the results. terminate() stops the worker immediately.

run() is serialized with toString() and executed inside the worker. That means it has no access to the outer scope:

  • no imports, no closures over variables in your module,
  • no this from the instance,
  • only its own params and the globals that exist inside a worker (self.postMessage(), Math, fetch() and so on).

Pass everything it needs through params, and send results back with self.postMessage().

Combine a thread with the EventBus: onMessage() sends an event, and the view subscribes to it.

src/Stats.js
import { View, EventBus, layout } from "jops-core";
import PrimeThread from "./PrimeThread.js";
export default class Stats extends View {
constructor() {
super();
this.layout = layout`<p id="primes">Counting…</p>`;
this.inflate();
}
onLayout() {
EventBus.subscribe(this, "primes:done", (count) => {
this.get("primes").set(layout`${count} primes`);
});
this.thread = new PrimeThread();
this.thread.start({ limit: 1_000_000 });
}
destroy() {
this.thread.terminate();
super.destroy();
}
}