Skip to content

Workers

Workers in the context of the Sodacore framework are like web-workers, a way to run code in a separate thread to avoid blocking the main thread.

Note: Workers run code in a separate thread, meaning that they do not share memory with the main thread, so you cannot access/inject services, providers, etc... within a worker, anything you want will need to be manually instantiated within the worker, assume a worker is a black-box.

Workers are, within the Sodacore framework, simple classes, the decorator does all the magic for you, by interacting with the workers handler to deal with spawning, messaging, and terminating workers.

Creating a worker

To create a worker, simply create a class, and use the @Worker() decorator, for example:

ts
import { BaseWorker, Expose, Utils, Worker } from '@sodacore/core';

@Worker(Utils.resolve(import.meta.filename), {
	poolSize: 2, // Optional, defaults to 1, internally all requests are queued and handled by the next available worker.
})
export class MyWorker extends BaseWorker {

	@Expose()
	public async doWork(data: string) {
		return `Worker processed: ${data}`;
	}
}

That's it, we create our worker class, decorate the class with the @Worker() decorator, passing in the current file (using Utils.resolve(import.meta.filename) to get the absolute path) and some options, in this case, we set the pool size to 2, meaning that we can have up to 2 instances of this worker running at any one time.

We then @Expose() any methods we want to be executed by the worker.

Note: The BaseWorker class has some useful methods and properties, such as logging, etc so that you don't need to re-implement them.

Using a worker

To use a worker, it's super simple, you just import the worker class, and execute the method using the standard DI.

ts
import { Controller, Get } from '@sodacore/http';
import { Inject } from '@sodacore/di';
import { MyWorker } from '../workers/my-worker';

@Controller('/worker')
export class WorkerController {
	@Inject() private myWorker!: MyWorker;

	@Get('/do-work')
	public async doWork() {
		const result = await this.myWorker.doWork('Hello World');
		return { result };
	}
}

And that's it.

Useful Notes

  • You may ask, what if I need to initialise something within the worker, maybe I want to connect to a database, or load some files ready for being tasked. Workers when created will attempt to look for the init method, if it exists, it will be called when the worker is first created, so you can use this to do any initialisation you need.

  • Also please note that all requests should be asynchonous, even if the method you define is synchronous, it will be wrapped in a promise, because it uses IPC under the hood to communicate between the main thread and the worker thread, so it's a good habit, to all exposed methods as async.

Released under the Apache-2.0 License.