Web workers
Web workers are a browser feature that allows running client-side code in parallel. Every worker runs on a separate thread, and they communicate by posting messages to one another.
In WebSharper, creating a web worker is very simple; much simpler, in fact, than in plain JavaScript.
Creating a Worker
You can call the WebSharper.JavaScript.Worker
constructor and pass it a function, which will be the entry point for the worker.
The above code will:
-
create and start a new web worker.
-
print the following from the created worker thread:
-
send the following from the worker thread to the main thread, which then prints it:
-
terminate the worker thread.
What's happening behind the scenes
A lot of things are happening behind the scenes with this simple call. The WebSharper compiler:
-
recognizes that you are calling the
Worker
constructor with a function argument, and sets this function (which we will call the entry point) aside. -
creates a separate compiled JavaScript file called
MyProject.worker.js
that only contains the entry point and any other functions, classes, etc that it requires. It passes as argument to the entry point the global scope of the worker. -
compiles the
Worker
constructor call into JavaScript as:
The path used may be not this simple, see below in customizing the script location
If you have several such Worker
calls in the same project, subsequent ones will be compiled to JavaScript files called MyProject.worker0.js
, MyProject.worker1.js
, and so on, unless a name
argument is provided for the Worker
constructor which will be used for naming the JavaScript file.
Message passing
Communication between a Worker and the main thread is done by message passing. The worker can listen for incoming messages from the main thread using self.OnMessage <- f
or self.AddEventListener("message", f, false)
, and send messages to the main thread using self.PostMessage(msg)
. Conversely, the main thread can listen for incoming messages from the worker using worker.OnMessage <- f
or self.AddEventListener("message", f, false)
, and send messages to the worker using worker.PostMessage(msg)
.
Using external dependencies
It is possible to use external script dependencies in a Worker.
If these are module or npm-based dependencies, they will be compiled to import
s in the worker script.
For example, here is a worker that uses math.js and the binding WebSharper.MathJS to perform mathematical operations:
If you are using non-module JavaScript dependencies, it will use a helper function called LoadScript
that fetches the script and evaluates it.
If you are using esbuild, make sure the following section is present in your esbuild.config.mjs
:
Customizing the worker script
There are a few options to customize the generated script for a worker.
-
You can customize the filename of the script by passing a string as first argument to the
Worker
constructor: -
You can decide to include the values, modules and types marked
[<JavaScriptExport>]
in the generated script by passing an additionaltrue
argument to theWorker
constructor.This can be useful for example if you intend to also call the generated script manually from JavaScript.
Note that in both cases, you must use a literal string or boolean, rather than a more complex expression, because it must be recognized at compile time.
Customizing the script location
The behind-the-scenes section above states that the worker constructor call is translated into the following JavaScript:
That is actually a simplification. In practice, WebSharper needs to figure out the path to the script: the file name is not sufficient. The full path is computed and depends on the type of final project that the code is used in:
- In a client-server
website
project, the default path iswwwroot/Scripts/WebSharper/workers/<filename>
. - In a single-page application (aka
bundle
orbundleOnly
project), the default path iswwwroot/Content/workers/<filename>
. - In a generated static HTML site (aka
html
project), the default path is/Scripts/workers/<filename>
.
These correspond to the location where the compiler extracts the files, so in a standard setup, everything just works and there is nothing to do. But if you need it, this is customizable using the scriptBaseUrl
configuration setting. It changes the base URL for the script, ie. the paths mentioned aboved minus the workers/<filename>
suffix. Note that this URL must end with a slash, and almost always needs to start with a slash too.
Here is an example wsconfig.json
that deviates from the standard: the output directory into which the files are extracted is customized using outputDir
, so we need to customize scriptBaseUrl
too.