From bae2ca3cca70f7989e9cb9f3f58958c2cb463e6e Mon Sep 17 00:00:00 2001 From: Tommaso Tessarolo Date: Sun, 3 Apr 2022 08:00:45 +0200 Subject: [PATCH] Updated Documentation for worker import URL --- README.md | 34 +++++----- docs/usage-advanced.md | 86 ++++++++++++------------ docs/usage-observables.md | 90 ++++++++++++------------- docs/usage-pool.md | 67 +++++++++--------- docs/usage.md | 138 +++++++++++++++++++------------------- 5 files changed, 210 insertions(+), 205 deletions(-) diff --git a/README.md b/README.md index 047eb756..ea22b23c 100644 --- a/README.md +++ b/README.md @@ -15,11 +15,11 @@ Uses web workers in the browser, `worker_threads` in node 12+ and [`tiny-worker` ### Features -* First-class support for **async functions** & **observables** -* Write code once, run it **on all platforms** -* Manage bulk task executions with **thread pools** -* Use **require()** and **import**/**export** in workers -* Works great with **webpack** +- First-class support for **async functions** & **observables** +- Write code once, run it **on all platforms** +- Manage bulk task executions with **thread pools** +- Use **require()** and **import**/**export** in workers +- Works great with **webpack** ### Version 0.x @@ -31,7 +31,7 @@ You can find the old version 0.12 of threads.js on the [`v0` branch](https://git npm install threads tiny-worker ``` -*You only need to install the `tiny-worker` package to support node.js < 12. It's an optional dependency and used as a fallback if `worker_threads` are not available.* +_You only need to install the `tiny-worker` package to support node.js < 12. It's an optional dependency and used as a fallback if `worker_threads` are not available._ ## Platform support @@ -42,7 +42,7 @@ npm install threads tiny-worker Running code using threads.js in node works out of the box. -Note that we wrap the native `Worker`, so `new Worker("./foo/bar")` will resolve the path relative to the module that calls it, not relative to the current working directory. +Note that we wrap the native `Worker`, so `new Worker(new URL("./foo/bar", import.meta.url))` will resolve the path relative to the module that calls it, not relative to the current working directory. That aligns it with the behavior when bundling the code with webpack or parcel. @@ -147,26 +147,26 @@ Everything else should work out of the box. ```js // master.js -import { spawn, Thread, Worker } from "threads" +import { spawn, Thread, Worker } from "threads"; -const auth = await spawn(new Worker("./workers/auth")) -const hashed = await auth.hashPassword("Super secret password", "1234") +const auth = await spawn(new Worker(new URL("./workers/auth", import.meta.url))); +const hashed = await auth.hashPassword("Super secret password", "1234"); -console.log("Hashed password:", hashed) +console.log("Hashed password:", hashed); -await Thread.terminate(auth) +await Thread.terminate(auth); ``` ```js // workers/auth.js -import sha256 from "js-sha256" -import { expose } from "threads/worker" +import sha256 from "js-sha256"; +import { expose } from "threads/worker"; expose({ hashPassword(password, salt) { - return sha256(password + salt) - } -}) + return sha256(password + salt); + }, +}); ``` ### spawn() diff --git a/docs/usage-advanced.md b/docs/usage-advanced.md index 6824ff37..7b5067ad 100644 --- a/docs/usage-advanced.md +++ b/docs/usage-advanced.md @@ -11,31 +11,32 @@ aside: ## Transferable objects -Use `Transfer()` to mark [transferable objects](https://developer.mozilla.org/en-US/docs/Web/API/Web_Workers_API/Using_web_workers#Passing_data_by_transferring_ownership_(transferable_objects)) like ArrayBuffers to be transferred to the receiving thread. It can speed up your code a lot if you are working with big pieces of binary data. +Use `Transfer()` to mark [transferable objects]() like ArrayBuffers to be transferred to the receiving thread. It can speed up your code a lot if you are working with big pieces of binary data. `Transfer()` comes in two flavors: -* `Transfer(myBuffer: Transferable)` -* `Transfer(arrayOrObjectContainingBuffers: any, [myBuffer]: Transferable[])` + +- `Transfer(myBuffer: Transferable)` +- `Transfer(arrayOrObjectContainingBuffers: any, [myBuffer]: Transferable[])` Use it when calling a thread function or returning from a thread function: ```js // master.js -import { spawn, Transfer, Worker } from "threads" +import { spawn, Transfer, Worker } from "threads"; -const xorBuffer = await spawn(new Worker("./workers/arraybuffer-xor")) -const resultBuffer = await xorBuffer(Transfer(testData), 127) +const xorBuffer = await spawn(new Worker(new URL("./workers/arraybuffer-xor", import.meta.url))); +const resultBuffer = await xorBuffer(Transfer(testData), 127); ``` ```js // workers/arraybuffer-xor.js -import { expose, Transfer } from "threads/worker" +import { expose, Transfer } from "threads/worker"; expose(function xorBuffer(buffer, value) { - const view = new Uint8Array(buffer) - view.forEach((byte, offset) => view.set([byte ^ value], offset)) - return Transfer(buffer) -}) + const view = new Uint8Array(buffer); + view.forEach((byte, offset) => view.set([byte ^ value], offset)); + return Transfer(buffer); +}); ``` Without `Transfer()` the buffers would be copied on every call and every return. Using `Transfer()` their ownership is transferred to the other thread instead only, to make sure it is accessible in a thread-safe way. This is a much faster operation. @@ -43,16 +44,16 @@ Without `Transfer()` the buffers would be copied on every call and every return. You can use transferable objects with observables, too. ```js -import { expose, Observable, Transfer } from "threads/worker" -import { DataSource } from "./my-data-source" +import { expose, Observable, Transfer } from "threads/worker"; +import { DataSource } from "./my-data-source"; expose(function streamBuffers() { - return new Observable(observer => { - const datasource = new DataSource() - datasource.on("data", arrayBuffer => observer.next(Transfer(arrayBuffer))) - return () => datasource.close() - }) -}) + return new Observable((observer) => { + const datasource = new DataSource(); + datasource.on("data", (arrayBuffer) => observer.next(Transfer(arrayBuffer))); + return () => datasource.close(); + }); +}); ``` ## Task queue @@ -66,17 +67,17 @@ Threads.js does not provide a distinct task queue implementation, but it comes w Every spawned thread emits events during its lifetime that you can subscribe to. This can be useful for debugging. ```js -import { spawn, Thread, Worker } from "threads" +import { spawn, Thread, Worker } from "threads"; -const myThread = await spawn(new Worker("./mythread")) +const myThread = await spawn(new Worker(new URL("./mythread", import.meta.url))); -Thread.events(myThread).subscribe(event => console.log("Thread event:", event)) +Thread.events(myThread).subscribe((event) => console.log("Thread event:", event)); ``` There is a specialized function to subscribe only to thread error events: ```js -Thread.errors(myThread).subscribe(error => console.log("Thread error:", error)) +Thread.errors(myThread).subscribe((error) => console.log("Thread error:", error)); ``` ## Custom message serializers @@ -88,18 +89,18 @@ You can however define and register custom serializers to provide support for pa First you need to implement your serializer. Fortunately, this is pretty straight-forward. ```typescript -import { SerializerImplementation } from "threads" +import { SerializerImplementation } from "threads"; interface SerializedMyClass { - __type: "$$MyClass" - state: string + __type: "$$MyClass"; + state: string; } class MyClass { - state: string + state: string; constructor(initialState: string) { - this.state = initialState + this.state = initialState; } doStuff() { @@ -109,41 +110,41 @@ class MyClass { serialize(): SerializedMyClass { return { __type: "$$MyClass", - state: this.state - } + state: this.state, + }; } static deserialize(message: SerializedMyClass) { - return new MyClass(message.state) + return new MyClass(message.state); } } const MySerializer: SerializerImplementation = { deserialize(message, defaultHandler) { if (message && message.__type === "$$MyClass") { - return MyClass.deserialize(message as any) + return MyClass.deserialize(message as any); } else { - return defaultHandler(message) + return defaultHandler(message); } }, serialize(thing, defaultHandler) { if (thing instanceof MyClass) { - return thing.serialize() + return thing.serialize(); } else { - return defaultHandler(thing) + return defaultHandler(thing); } - } -} + }, +}; ``` Finally, register your serializer in both the main thread and the worker. Register it early, before you `spawn()` or `expose()` anything. ```typescript -import { registerSerializer } from "threads" +import { registerSerializer } from "threads"; // also exported from the worker sub-module: // import { registerSerializer } from "threads/worker" -registerSerializer(MySerializer) +registerSerializer(MySerializer); ``` You can also register multiple serializers. Just call `registerSerializer()` multiple times – make sure to register the same serializers in the worker and main thread. @@ -151,16 +152,15 @@ You can also register multiple serializers. Just call `registerSerializer()` mul The registered serializers will then be chained. The serializer that was registered at last is invoked first. If it does not know how to serialize the data, it will call its fallback handler which is the second-to-last serializer and so forth. ```typescript -import { registerSerializer } from "threads" +import { registerSerializer } from "threads"; -registerSerializer(SomeSerializer) -registerSerializer(AnotherSerializer) +registerSerializer(SomeSerializer); +registerSerializer(AnotherSerializer); // threads.js will first try to use AnotherSerializer, will fall back to SomeSerializer, // eventually falls back to passing the data as is if no serializer can handle it ``` - ## Debug logging We are using the [`debug`](https://github.com/visionmedia/debug) package to provide opt-in debug logging. All the package's debug messages have a scope starting with `threads:`, with different sub-scopes: diff --git a/docs/usage-observables.md b/docs/usage-observables.md index 1dff95d1..52ff43a2 100644 --- a/docs/usage-observables.md +++ b/docs/usage-observables.md @@ -17,28 +17,28 @@ You can return observables in your worker. It works fully transparent - just sub ```js // master.js -import { spawn, Thread, Worker } from "threads" +import { spawn, Thread, Worker } from "threads"; -const counter = await spawn(new Worker("./workers/counter")) +const counter = await spawn(new Worker(new URL("./workers/counter", import.meta.url))); -counter().subscribe(newCount => console.log(`Counter incremented to:`, newCount)) +counter().subscribe((newCount) => console.log(`Counter incremented to:`, newCount)); ``` ```js // workers/counter.js -import { Observable } from "observable-fns" -import { expose } from "threads/worker" +import { Observable } from "observable-fns"; +import { expose } from "threads/worker"; function startCounting() { - return new Observable(observer => { + return new Observable((observer) => { for (let currentCount = 1; currentCount <= 10; currentCount++) { - observer.next(currentCount) + observer.next(currentCount); } - observer.complete() - }) + observer.complete(); + }); } -expose(startCounting) +expose(startCounting); ``` ### Hot observables @@ -56,14 +56,14 @@ Using `Subject` we can create objects that implement the `Observable` interface, In a nutshell: ```js -const observable = new Observable(observer => { +const observable = new Observable((observer) => { // We can call `.next()`, `.error()`, `.complete()` only here // as they are only exposed on the `observer` - observer.complete() -}) + observer.complete(); +}); -const subject = new Subject() -subject.complete() +const subject = new Subject(); +subject.complete(); // We are free to call `.next()`, `.error()`, `.complete()` from anywhere now // Beware: With great power comes great responsibility! Don't write spaghetti code. ``` @@ -71,11 +71,11 @@ subject.complete() Subscribing still works the same: ```js -const subscriptionOne = observable.subscribe(/* ... */) -subscriptionOne.unsubscribe() +const subscriptionOne = observable.subscribe(/* ... */); +subscriptionOne.unsubscribe(); -const subscriptionTwo = subject.subscribe(/* ... */) -subscriptionTwo.unsubscribe() +const subscriptionTwo = subject.subscribe(/* ... */); +subscriptionTwo.unsubscribe(); ``` To get a plain observable that proxies all values, errors, completion of the @@ -83,7 +83,7 @@ subject, but does not expose the `.next()`, ... methods, use `Observable.from()` ```js // The returned observable will be read-only -return Observable.from(subject) +return Observable.from(subject); ``` ## Streaming results @@ -92,50 +92,50 @@ We can easily use observable subjects to stream results as they are computed. ```js // master.js -import { spawn, Thread, Worker } from "threads" +import { spawn, Thread, Worker } from "threads"; -const minmax = await spawn(new Worker("./workers/minmax")) +const minmax = await spawn(new Worker(new URL("./workers/minmax", import.meta.url))); minmax.values().subscribe(({ min, max }) => { - console.log(`Min: ${min} | Max: ${max}`) -}) + console.log(`Min: ${min} | Max: ${max}`); +}); -await minmax.add(2) -await minmax.add(3) -await minmax.add(4) -await minmax.add(1) -await minmax.add(5) -await minmax.finish() +await minmax.add(2); +await minmax.add(3); +await minmax.add(4); +await minmax.add(1); +await minmax.add(5); +await minmax.finish(); -await Thread.terminate(minmax) +await Thread.terminate(minmax); ``` ```js // minmax.js -import { Observable, Subject } from "threads/observable" -import { expose } from "threads/worker" +import { Observable, Subject } from "threads/observable"; +import { expose } from "threads/worker"; -let max = -Infinity -let min = Infinity +let max = -Infinity; +let min = Infinity; -let subject = new Subject() +let subject = new Subject(); const minmax = { finish() { - subject.complete() - subject = new Subject() + subject.complete(); + subject = new Subject(); }, add(value) { - max = Math.max(max, value) - min = Math.min(min, value) - subject.next({ max, min }) + max = Math.max(max, value); + min = Math.min(min, value); + subject.next({ max, min }); }, values() { - return Observable.from(subject) - } -} + return Observable.from(subject); + }, +}; -expose(minmax) +expose(minmax); ``` And there we go! A simple worker that keeps track of the minimum and maximum value passed to it, yielding observable updates we can subscribe to. The updated values will be streamed as they happen. diff --git a/docs/usage-pool.md b/docs/usage-pool.md index 4c683570..185313ca 100644 --- a/docs/usage-pool.md +++ b/docs/usage-pool.md @@ -16,17 +16,20 @@ A `Pool` allows you to create a set of workers and queue worker calls. The queue Use it if you have a lot of work to offload to workers and don't want to drown them in a pile of work at once, but run those tasks in a controlled way with limited concurrency. ```js -import { spawn, Pool, Worker } from "threads" +import { spawn, Pool, Worker } from "threads"; -const pool = Pool(() => spawn(new Worker("./workers/multiplier")), 8 /* optional size */) +const pool = Pool( + () => spawn(new Worker(new URL("./workers/multiplier", import.meta.url))), + 8 /* optional size */ +); -pool.queue(async multiplier => { - const multiplied = await multiplier(2, 3) - console.log(`2 * 3 = ${multiplied}`) -}) +pool.queue(async (multiplier) => { + const multiplied = await multiplier(2, 3); + console.log(`2 * 3 = ${multiplied}`); +}); -await pool.completed() -await pool.terminate() +await pool.completed(); +await pool.terminate(); ``` Note that `pool.queue()` will schedule a task to be run in a deferred way. It might execute straight away or it might take a while until a new worker thread becomes available. @@ -35,14 +38,14 @@ Note that `pool.queue()` will schedule a task to be run in a deferred way. It mi ```ts interface PoolOptions { - concurrency?: number - maxQueuedJobs?: number - name?: string - size?: number + concurrency?: number; + maxQueuedJobs?: number; + name?: string; + size?: number; } -function Pool(threadFactory: () => Thread, size?: number): Pool -function Pool(threadFactory: () => Thread, options?: PoolOptions): Pool +function Pool(threadFactory: () => Thread, size?: number): Pool; +function Pool(threadFactory: () => Thread, options?: PoolOptions): Pool; ``` The first argument passed to the `Pool()` factory must be a function that spawns a worker thread of your choice. The pool will use this function to create its workers. @@ -63,7 +66,7 @@ type TaskFunction = (thread: ThreadType) => Promise | T pool.queue(task: TaskFunction): Promise ``` -The promise returned by `pool.queue()` resolves or rejects when the queued task function has been run and resolved / rejected. That means *you should usually not `await` that promise straight away* when calling `pool.queue()`, since the code after this line will then not be run until the task has been run and completed. +The promise returned by `pool.queue()` resolves or rejects when the queued task function has been run and resolved / rejected. That means _you should usually not `await` that promise straight away_ when calling `pool.queue()`, since the code after this line will then not be run until the task has been run and completed. Whenever a pool worker finishes a job, the next pool job is de-queued (that is the function you passed to `pool.queue()`). It is called with the worker as the first argument. The job function is supposed to return a promise - when this promise resolves, the job is considered done and the next job is de-queued and dispatched to the worker. @@ -74,17 +77,17 @@ The promise returned by `pool.completed()` will resolve once the scheduled callb Track a pooled task via the object that the `pool.queue()` promise resolves to. You can `await pool.queue()` to obtain the job's result. Be aware, though, that if you `await` the result directly on queueing, you will only queue another job after this one has finished. You might rather want to `pool.queue().then()` to defer handling the outcome and keep queueing tasks uninterruptedly. ```js -import { spawn, Pool, Worker } from "threads" +import { spawn, Pool, Worker } from "threads"; -const pool = Pool(() => spawn(new Worker("./workers/crytpo"))) -const task = pool.queue(crypto => crypto.encrypt("some-password")) +const pool = Pool(() => spawn(new Worker(new URL("./workers/crytpo", import.meta.url)))); +const task = pool.queue((crypto) => crypto.encrypt("some-password")); -task.then(result => { - // do something with the result -}) +task.then((result) => { + // do something with the result +}); -await pool.completed() -await pool.terminate() +await pool.completed(); +await pool.terminate(); ``` ## Cancelling a queued task @@ -92,18 +95,18 @@ await pool.terminate() You can cancel queued tasks, too. If the pool has already started to execute the task, you cannot cancel it anymore, though. ```js -const task = pool.queue(multiplierWorker => multiplierWorker(2, 3)) -task.cancel() +const task = pool.queue((multiplierWorker) => multiplierWorker(2, 3)); +task.cancel(); ``` ## Pool termination ```js // Terminate gracefully -pool.terminate() +pool.terminate(); // Force-terminate pool workers -pool.terminate(true) +pool.terminate(true); ``` By default the pool will wait until all scheduled tasks have completed before terminating the workers. Pass `true` to force-terminate the pool immediately. @@ -121,13 +124,13 @@ As outlined before, pool tasks provide a Promise-like `.then()` method. You can ```ts // (Created a pool and queued other pool tasks before…) -const myTasks: QueuedTask[] = [] +const myTasks: QueuedTask[] = []; for (let input = 0; input < 5; input++) { - const task = pool.queue(worker => worker.work(input)) - myTasks.push(task) + const task = pool.queue((worker) => worker.work(input)); + myTasks.push(task); } -await Promise.all(myTasks) -console.log("All worker.work() tasks have completed. Other pool tasks might still be running.") +await Promise.all(myTasks); +console.log("All worker.work() tasks have completed. Other pool tasks might still be running."); ``` diff --git a/docs/usage.md b/docs/usage.md index 32b58459..2343bc91 100644 --- a/docs/usage.md +++ b/docs/usage.md @@ -15,27 +15,27 @@ A trivial worker example to demo the two most important functions provided by th ```js // master.js -import { spawn, Thread, Worker } from "threads" +import { spawn, Thread, Worker } from "threads"; async function main() { - const add = await spawn(new Worker("./workers/add")) - const sum = await add(2, 3) + const add = await spawn(new Worker(new URL("./workers/add"), import.meta.url)); + const sum = await add(2, 3); - console.log(`2 + 3 = ${sum}`) + console.log(`2 + 3 = ${sum}`); - await Thread.terminate(add) + await Thread.terminate(add); } -main().catch(console.error) +main().catch(console.error); ``` ```js // workers/add.js -import { expose } from "threads/worker" +import { expose } from "threads/worker"; expose(function add(a, b) { - return a + b -}) + return a + b; +}); ``` ### spawn() @@ -60,25 +60,27 @@ This is one of two kinds of workers. A function worker exposes a single function ```js // master.js -import { spawn, Thread, Worker } from "threads" +import { spawn, Thread, Worker } from "threads"; -const fetchGithubProfile = await spawn(new Worker("./workers/fetch-github-profile")) -const andywer = await fetchGithubProfile("andywer") +const fetchGithubProfile = await spawn( + new Worker(new URL("./workers/fetch-github-profile", import.meta.url)) +); +const andywer = await fetchGithubProfile("andywer"); -console.log(`User "andywer" has signed up on ${new Date(andywer.created_at).toLocaleString()}`) +console.log(`User "andywer" has signed up on ${new Date(andywer.created_at).toLocaleString()}`); -await Thread.terminate(fetchGithubProfile) +await Thread.terminate(fetchGithubProfile); ``` ```js // workers/fetch-github-profile.js -import fetch from "isomorphic-fetch" -import { expose } from "threads/worker" +import fetch from "isomorphic-fetch"; +import { expose } from "threads/worker"; expose(async function fetchGithubProfile(username) { - const response = await fetch(`https://api.github.com/users/${username}`) - return response.json() -}) + const response = await fetch(`https://api.github.com/users/${username}`); + return response.json(); +}); ``` ### Module worker @@ -87,37 +89,37 @@ This is the second kind of worker. A module worker exposes an object whose value ```js // master.js -import { spawn, Thread, Worker } from "threads" +import { spawn, Thread, Worker } from "threads"; -const counter = await spawn(new Worker("./workers/counter")) -await counter.increment() -await counter.increment() -await counter.decrement() +const counter = await spawn(new Worker(new URL("./workers/counter", import.meta.url))); +await counter.increment(); +await counter.increment(); +await counter.decrement(); -console.log(`Counter is now at ${await counter.getCount()}`) +console.log(`Counter is now at ${await counter.getCount()}`); -await Thread.terminate(counter) +await Thread.terminate(counter); ``` ```js // workers/counter.js -import { expose } from "threads/worker" +import { expose } from "threads/worker"; -let currentCount = 0 +let currentCount = 0; const counter = { getCount() { - return currentCount + return currentCount; }, increment() { - return ++currentCount + return ++currentCount; }, decrement() { - return --currentCount - } -} + return --currentCount; + }, +}; -expose(counter) +expose(counter); ``` ### Error handling @@ -126,20 +128,20 @@ Works fully transparent - the promise in the master code's call will be rejected ```js // master.js -import { spawn, Thread, Worker } from "threads" +import { spawn, Thread, Worker } from "threads"; -const counter = await spawn(new Worker("./workers/counter")) +const counter = await spawn(new Worker(new URL("./workers/counter", import.meta.url))); try { - await counter.increment() - await counter.increment() - await counter.decrement() + await counter.increment(); + await counter.increment(); + await counter.decrement(); - console.log(`Counter is now at ${await counter.getCount()}`) + console.log(`Counter is now at ${await counter.getCount()}`); } catch (error) { - console.error("Counter thread errored:", error) + console.error("Counter thread errored:", error); } finally { - await Thread.terminate(counter) + await Thread.terminate(counter); } ``` @@ -154,13 +156,13 @@ There is also a convenience function `BlobWorker.fromText()` that creates a new Here is a webpack-based example, leveraging the `raw-loader` to inline the worker code. The worker code that we load using the `raw-loader` is the content of bundles that have been created by two previous webpack runs: one worker build targetting node.js, one for web browsers. ```js -import { spawn, BlobWorker } from "threads" -import MyWorkerNode from "raw-loader!../dist/worker.node/worker.js" -import MyWorkerWeb from "raw-loader!../dist/worker.web/worker.js" +import { spawn, BlobWorker } from "threads"; +import MyWorkerNode from "raw-loader!../dist/worker.node/worker.js"; +import MyWorkerWeb from "raw-loader!../dist/worker.web/worker.js"; -const MyWorker = process.browser ? MyWorkerWeb : MyWorkerNode +const MyWorker = process.browser ? MyWorkerWeb : MyWorkerNode; -const worker = await spawn(BlobWorker.fromText(MyWorker)) +const worker = await spawn(BlobWorker.fromText(MyWorker)); // Now use this worker as always ``` @@ -174,58 +176,58 @@ When using TypeScript you can declare the type of a `spawn()`-ed worker: ```ts // master.ts -import { spawn, Thread, Worker } from "threads" +import { spawn, Thread, Worker } from "threads"; -type HashFunction = (input: string) => Promise +type HashFunction = (input: string) => Promise; -const sha512 = await spawn(new Worker("./workers/sha512")) -const hashed = await sha512("abcdef") +const sha512 = await spawn(new Worker(new URL("./workers/sha512", import.meta.url))); +const hashed = await sha512("abcdef"); ``` It's also easy to export the type from the worker module and use it when `spawn()`-ing: ```ts // master.ts -import { spawn, Thread, Worker } from "threads" -import { Counter } from "./workers/counter" +import { spawn, Thread, Worker } from "threads"; +import { Counter } from "./workers/counter"; -const counter = await spawn(new Worker("./workers/counter")) -console.log(`Initial counter: ${await counter.getCount()}`) +const counter = await spawn(new Worker(new URL("./workers/counter", import.meta.url))); +console.log(`Initial counter: ${await counter.getCount()}`); -await counter.increment() -console.log(`Updated counter: ${await counter.getCount()}`) +await counter.increment(); +console.log(`Updated counter: ${await counter.getCount()}`); -await Thread.terminate(counter) +await Thread.terminate(counter); ``` ```ts // counter.ts -import { expose } from "threads/worker" +import { expose } from "threads/worker"; -let currentCount = 0 +let currentCount = 0; const counter = { getCount() { - return currentCount + return currentCount; }, increment() { - return ++currentCount + return ++currentCount; }, decrement() { - return --currentCount - } -} + return --currentCount; + }, +}; -export type Counter = typeof counter +export type Counter = typeof counter; -expose(counter) +expose(counter); ``` ### TypeScript workers in node.js You can spawn `*.ts` workers out-of-the-box without prior transpiling if ts-node is installed. -If the path passed to `new Worker()` resolves to a `*.ts` file, threads.js will check if `ts-node` is available. If so, it will create an in-memory module that wraps the actual worker module and initializes `ts-node` before running the worker code. *It is likely you will have to increase the THREADS_WORKER_INIT_TIMEOUT environment variable (milliseconds, default 10000) to account for the longer ts-node startup time if you see timeouts spawning threads.* +If the path passed to `new Worker()` resolves to a `*.ts` file, threads.js will check if `ts-node` is available. If so, it will create an in-memory module that wraps the actual worker module and initializes `ts-node` before running the worker code. _It is likely you will have to increase the THREADS_WORKER_INIT_TIMEOUT environment variable (milliseconds, default 10000) to account for the longer ts-node startup time if you see timeouts spawning threads._ In case `ts-node` is not available, `new Worker()` will attempt to load the same file, but with a `*.js` extension. It is then in your hands to transpile the worker module before running the code.