Worker Communication
The webR worker thread
WebR initialises by launching a version of R built for WebAssembly in a JavaScript web worker. This allows R to perform long running computation without blocking the main browser thread. Without this process, the browser page loading webR would freeze whenever R is performing work.
Communication channels
Communication between the main thread and the webR worker thread is managed by message passing through a communication channel. The possible channel types are,
Name | Requirements | Limitations |
---|---|---|
SharedArrayBuffer (Default) |
Cross-origin Isolation | None |
ServiceWorker |
JavaScript service workers | A service worker script must be hosted in the same origin as the page loading webR. |
A communication channel will be automatically selected at startup, defaulting to SharedArrayBuffer
if the page is cross-origin isolated. It is also possible to manually select a channel type by setting the WebROptions.channelType
configuration option at startup.
JavaScript promises
Since messages and data are communicated to and from the worker thread asynchronously, most of the API methods provided by webR return results through JavaScript Promises rather than returning results directly.
In a modern asynchronous JavaScript environment (e.g. within an async function
), the result of a promise can easily be obtained by using the await
keyword. For example,
await webR.read();
{ type: 'stdout', data: 'R is a collaborative project with many contributors.' }
Alternative use of promises
As an alternative, the then()
method can be used with JavaScript promises and callbacks when not in an asynchronous environment to obtain results without requiring the use of async
or await
,
.read().then((message) => {
webRconsole.log(message);
; })
{ type: 'stdout', data: 'R is a collaborative project with many contributors.' }
Message passing and requests
Communication between the main thread and the webR worker thread is in the form of messages sent over an established communication channel. A web application making use of webR should send input messages and handle output messages.
The webR main thread maintains both input and an output queues. All messages sent from the main thread to the worker are stored in the input queue until they are consumed by the webR worker thread. If the input queue is empty, the worker worker thread blocks until an input message has been received.
Messages from the worker thread are stored in the output queue on the main thread until they are read and consumed by the application loading and using webR.
Message interface
Communication messages are in the form of a JavaScript object with type Message
, which in general have shape,
type: string, data: any } {
From the main thread, messages can be sent to the webR worker thread using WebR.write()
.
Messages from the worker thread can be obtained from the output queue using WebR.read()
to receive the next message in the queue, or WebR.flush()
to receive an array of all queued output messages. These methods both return promises resolving to the output messages.
The WebR.close()
method closes down the communication channel. This stops the webR worker thread and should only be used once communication with an R process is no longer required.
Handling messages
A useful pattern for a web application making use of webR is to handle output messages in an asynchronous loop,
async run() {
for (;;) {
const output = await webR.read();
switch (output.type) {
case 'stdout':
console.log(output.data);
break;
case 'stderr':
console.error(output.data);
break;
default:
console.warn(`Unhandled output type: ${output.type}.`);
}
} }
As an alternative to manually managing messages in this way, the Console
class is available on the main WebR
module which can be used to implement a simple R console using JavaScript callbacks.
Input messages
Standard input stream
Messages of type stdin
are sent to the R standard input stream, and a convenience function WebR.writeConsole()
is provided to send messages of this type.
Messages of type stdin
should provide a line of input in the form of a string as the message data.
type: 'stdin', data: string } {
Package installation
A request to install a package from the webR binary package repository can be made with messages of type installPackage
, providing the name of the requested package to be installed in the form of a string,
type: 'installPackage', data: { name: string } } {
A convenience function WebR.installPackages()
is provided, taking an array of package names to be installed.
Interruption
A long running R computation can be interrupted by sending an interruption request message using the WebR.interrupt()
method.
Ouput messages
Standard output streams
Messages of type stdout
or stderr
are sent to the output queue for each line of standard stream output given by R. The line of output is given in the data
property.
type: 'stdout' | 'stderr', data: string } {
Prompt
Messages of type prompt
are sent to the output queue when R has given a prompt and is waiting for input. The prompt string, e.g. '> '
, is given in the data
property.
type: 'prompt', data: string } {
Plotting with the webR HTML canvas
WebR’s version of R built for WebAssembly includes a built in graphics device, canvas()
, designed for drawing using the JavaScript Canvas API. This graphics device is implemented by sending messages of type canvasExec
to the main thread for processing.
The Canvas API messages are of the form,
type: 'canvasExec', data: string } {
where the data
property contains a string that is to be interpreted as a single Canvas API call. Canvas message data can be append to any 2D canvas context that is available on the main thread, and then executed by constructing a JavaScript Function()
.
For example, for a fixed HTML canvas element with id="plot-canvas"
, messages of type canvasExec
could be handled by executing,
Function(`
document.getElementById('plot-canvas').getContext('2d').${message.data}
`)();
Emscripten virtual filesystem
WebR runs under a virtual file system provided by Emscripten. Request messages can be sent from the main thread to interact with the virtual filesystem (VFS) through the WebRFS
interface. This interface is designed to broadly match that of the Emscripten File System API.
Read a file on the VFS
Get the contents of a file from the Emscripten VFS using the WebR.FS.readFile()
method. This method takes the full path name to a file in the form of a string as its argument and returns a JavaScript promise, resolving to the contents of the requested file provided in the form of a Uint8Array
.
Write a file to the VFS
Write a new file to the Emscripten VFS using the WebR.FS.writeFile()
method. In addition to the full path name as above, the content of the new file should be given as the method’s second argument in the form of a Uint8Array
.
This method returns a JavaScript promise, resolving once the file has been created.
Other VFS operations
Further details for similar virtual filesystem operations, including functions for removing files and working with directories, can be found in the WebRFS
interface reference.
Setting up an R REPL with Console
The Console
class, in combination with JavaScript callback functions, can be used as an alternative way to use the R REPL without directly issuing messages to the webR communication channel.
The issuing of input messages and the consumption of output messages over the communication channel between the webR worker thread and the main thread is handled by Console
, invoking the relevant callbacks provided.
The Console
class constructor takes two arguments,
callbacks
- A list of webR console callbacks to be used for this console.options
- The startup options to be provided to webR.
The Console.run()
method is used to start the R console. Callback functions stdout
and stderr
are called with a single line of output as the first argument when the R REPL produces standard stream output. The default implementation of stdout
and stderr
writes to the console using console.log
and console.error
.
R code can be sent as input by calling the Console.stdin()
method.
A long running R computation can be interrupted by calling the Console.interrupt()
method.
The prompt
callback function is called when webR produces a prompt at the REPL and is awaiting user input. The prompt character (usually >
or +
) is given as the first argument to the callback function. The default implementation of prompt
shows a JavaScript prompt asking the user for input, and then sends the user input to stdin
.
The canvasExec
callback function is called when webR writes plots to the built-in HTML canvas graphics device. Once the default HTML canvas has been written to (e.g. by plotting something in R), the HTML canvas element can be accessed with the Console.canvas
instance property.
See the Example Usage page to see a worked example of a HTML web page loading webR and starting a Console
, creating an interactive R REPL directly in the page.