You are here: Worker Thread API
Worker Thread API
Worker threads are a means to execute different tasks in multiple parallel contexts of execution in a concurrent manner, which can take advantage of multiprocessor and multithreaded environments as well as to keep UI Thread in Application responsive by delegating or offloading work which need not be handled in UI Main thread, to a different secondary thread.
Worker thread is a continuous parallel thread that runs and accepts messages until the time it is explicitly closed or terminated. Messages to a worker thread can be sent from the parent thread or its child worker threads. Through out this document, parent thread is referred as thread where a worker thread is spawned.
Worker can have logic that gets executed parallel for each of the messages that it receives. If worker thread is busy handling messages, incoming messages that it receives will be queued for processing.
Sharing data between parent thread and worker threads is done through message passing and by default variables, functions, or state is not shared.
The current specification is inspired from and based on HTML5 Web Worker threads standard, that use Message Passing model or mechanism for communication between threads.
Note: To use AWS in workerthreads, you must follow these steps.
- Copy all AWS related files from <workspace>/<appname>/cloudsdks folder into the <workspace>/<appname>/workerthreads folder of the project. This is because while importing the AWS files using Iris, the Volt MX HttpRequest and DOMParser related code are added to the AWS files. These modified files are then saved in the cloudsdks folder by Iris.
- After the files have been copied, use the
require(<aws files>)
code in the workerthread.js file to import all the AWS files into worker threads context.
AWS objects must be created in the worker thread. Any object created in the regular thread will not work in the worker thread.
The Worker Thread API contains the following API Elements:
Function | Description |
---|---|
voltmx.worker.hasWorkerThreadSupport | Determines whether the current platform environment has worker thread support. |
voltmx.worker.WorkerThread | Creates a WorkerThread object and returns a handle to it. The worker object represents a worker thread. |
Method | Description |
---|---|
addEventListener | Event Handlers can be registered using addEventListener() method on the worker Objects and once registered messages and errors from a worker thread can be received in parent thread. |
close | Worker thread can be terminated from inner scope of the worker by invoking close(). The worker thread is killed immediately without an opportunity to complete its operations or clean up. |
postMessage | postMessage() sends a JSON object or String message to the Parent/worker's scope by invoking respective registered "message" event handlers. |
removeEventListener | removeEventListener() is used to remove the previously registered message or error event listener that was registered using addEventListener(). |
terminate | When called from parent scope immediately terminates the worker. This does not offer the worker an opportunity to finish its operations. It is simply stopped at once. |
Scope
- <Volt MX Iris platform version >= 9.2
- Support for JavaScript.
- Supported mobile Platforms:
- iOS
- Android
- SPA: Supported List of browsers starting from versions.
- Supported List of Browsers:
SPA
iOS Android Native Android Chrome Windows 5.0-5.1 4.4 33.0 10 DesktopWeb
IE Firefox Chrome Safari 10.0 4.0 20 5.0 - Windows
Introduction to Constructor - WorkerThread()
- The WorkerThread() constructor creates and returns the handle to the newly created worker thread. The new worker thread can be used by the Parent thread for any further communication with the worker thread.
- To create a worker thread, it requires a JavaScript file name or a functional module name. The WorkerThread() constructor is invoked with the JavaScript file or a functional module name as its only argument and a worker thread instance is then created and returned.
- Worker threads may in turn initiate new worker threads.
- If a Javascript file name with ".js" extension is passed as WorkerThread() constructor argument, it looks up only in the
workerthreads
directory inmodules/js
path and loads if the file is found. This holds good for functional modules based projects as well as non-functional modules based projects. - If a function module name is provided as an argument for WorkerThread() constructor, in case of functional modules based project then the module will be loaded if found in the modules listing.
Worker Thread Scenarios
The scenarios of using WorkerThread() constructor are as follows:
-
The WorkerThread() constructor creates a new worker thread and returns a handle to the new worker thread, which can be used by the parent thread for any further communication with the worker thread.
Creating a worker thread requires a JavaScript file name or a functional module name. The WorkerThread() constructor is invoked with the JavaScript file or a functional module name as its only argument and a worker thread instance is then created and returned:
var worker = new voltmx.worker.WorkerThread('helper.js'); var worker = new voltmx.worker.WorkerThread('functionModuleName');
-
A message event handler can be registered with the worker by parent thread to receive messages from the worker thread.
worker.addEventListener("message", function (event) { ... });
- To send data from parent to a worker, postMessage() method can be used from parent.
worker.postMessage({ operation: 'find-edges', input: 'buffer', threshold: 0.6 } );
- To send messages back from worker thread to parent thread scope, postMessage() can be used.
postMessage({'msg':'Data'});
- To receive a messages inside the worker thread from parent thread, the message event handler can be registered using addEventListener() inside worker thread.
self.addEventListener( "message", function (event) { ... });
Worker Thread Life Cycle
The following steps provide the work flow to use worker thread:
- Call to Worker constructor will create a new Worker instance and a new parallel execution environment context is created, and immediately starts execution in the new parallel thread of control in an asynchronous manner. In this new thread, first the Worker will try to load the ‘workerjs’ script.
- As a result of the asynchronous parallel nature of execution in worker thread context, invocation of Worker constructor call in Parent thread will return a new Worker instance handle and Parent proceeds with execution of next instructions.
- Every Worker thread will have its own event loop which takes care of the execution of all the received message tasks which are queued for this worker in that order until ‘self.close()’ in worker scope or ‘worker.terminate()’ in parent worker scope are invoked.
- From the moment of successful creation of worker thread and until ‘self.close()’ in worker scope or ‘worker.terminate()’ in parent worker scope are invoked, the worker thread will be alive and can receive and process messages which are sent to this worker form its parent or from its child workers if created, as well as it can send messages using postMessage() to its parent thread and its child worker threads if created.
message event handler
"message" event handler receives an "event" object which contains the JSON or string message that is passed to postMessage() during invocation and the same message can be accessed from its "event.data" field. The data passed to postMessage() should be a String or JSON object.
Adhering to the JSON standard, the JSON object passed to postMessage() API should be serializable JSON without opaque object handles or function object handles etc. The data which is passed between the parent thread and worker thread using postMessage() API are copied, not shared, so the end result is that a duplicate is created on each end.
Multiple "message" event handlers can also be registered in Parent scope and in workers inner scope and all the registered event handlers will be invoked in the registered order whenever a postMessage() is called.
Syntax
function(event) { });
Input Parameters
String / JSON Object
- "message" event handler receives an "event" object which contains the JSON or string message that is passed to postMessage() during invocation and the same message can be accessed from its "event.data" field.
- The data passed to postMessage() should be a String or JSON object.
- Adhering to the JSON standard, the JSON object passed to postMessage() API should be serializable JSON without opaque object handles or function object handles etc.
- The data which is passed between the parent thread and worker thread using postMessage() API are copied, not shared, so the end result is that a duplicate is created on each end.
- Multiple "message" event handlers can also be registered in Parent scope and in workers inner scope and all the registered event handlers will be invoked in the registered order whenever a postMessage() is called.
Example
var evtMessageHandler_1 = function(event) {
//In case of JSONvoltmx.print ("Received message :" + event.data["msg"]);"
//In case of string
voltmx.print ("Received message :" + event.data);
};
Platform Availability
Available for iOS, Android, Windows, SPA, and Desktop Web. For more information, see Scope.
Importing scripts
Worker threads can use importScripts() function to import external scripts their scope by providing the JS file name to import. This method takes one or more JavaScript file names to import.
This API is only available in worker thread scope and not in main parent thread scope.
In case of Functional modules based project " voltmx.modules.loadFunctionalModule()" API can be used to import a functional module into workers scope. Refer Functional Modules specification document for usage help on loadFunctionalModule() API.
importScripts() if invoked with .js file, looks up only in the "workerthreads" directory under "modules/js/" in Volt MX Iris IDE Project structure to import scripts into workers scope. This holds good for both functional modules based projects and non-functional modules based projects.
In case of loading multiple files using importScripts(), if an error occurs while loading one of the script, then the remaining scripts are not loaded into context scope.
For more information on Functional Module APIs, refer Functional Modules APIs.
Syntax
importScripts(".js_file_name");
or
importScripts("functional_module_name");
Input Parameters
JSFileNames [Object]
or
Functional_Module_Name [Object]
- One or more comma separated list of JavaScript file names.
Example
importScripts("Utility.js"); // loads Util.js
importScripts("Utility1.js", "Utility2.js", "Utility3.js");
Return Values
None
Exceptions
Note: If no argument is given, no exception is raised and it does nothing.
When an error is encountered, the VoltMXError JS object is thrown with the following information:
Error Code | Name | Message | Reason |
---|---|---|---|
3002 | WorkerThreadError | importScripts: InvalidParameter. Invalid script name | This exception occurs when the argument passed is not a string. |
3002 | WorkerThreadError | importScripts: InvalidParameter. Unable to import script. |
This exception occurs when it is unable to find and load the JS script. |
- In worker scope, if these exceptions are not handled and if an error event handler is registered in worker’s inner scope or/and in parent scope for this worker object, then it is invoked with an error event object and its message attribute is set as follows:
Exception 1 - message: "importScripts: InvalidParameter. Invalid script name"
Exception 2 - message: "importScripts: InvalidParameter. Invalid script name"
Differences in behavior of importScripts() and __voltmx.modules.loadFunctionalModule_()* API with respect to Functional Modules:
Without Functional Modules | With Functional Modules |
---|---|
From inside Worker context if importScripts() is used to import external JS scripts the search criteria would be : only "workerthreads" directory. | From inside Worker context if importScripts() is used to import external JS scripts the search criteria would be : only "workerthreads" directory. |
voltmx.modules.loadFunctionalModule() function cannot be used in workers scope to load any FunctionalModule. | voltmx.modules.loadFunctionalModule() function can be used in workers scope to load any JavaScript script which is part of some Functional Module. |
Platform Availability
Available for iOS, Android, Windows, SPA, and Desktop Web. For more information, see Scope.
Using Worker Threads Feature
The following topics helps you to use the worker thread feature:
- Communicating and Data Processing Between Threads
- Nesting of Threads and Performing Parallel Tasks
- Scope Rules and Supported APIs
- FFI and Custom Widgets
- Guidelines and Limitations
- Debugger Support
Communicating and Data Processing Between Threads
Main.js
//create new worker
var worker = new voltmx.worker.WorkerThread('1_worker.js');
//invoked when worker calls postmessage() from its inner scope
worker.addEventListener("message", function (event) {
voltmx.print('Parent Scope : onmessage : event.data : ' + event.data["message"]);
});
voltmx.print('Parent Scope : Invoking worker.postmessage()');
//will invoke worker's inner scope onmessage()
worker.postMessage({
'message': 'Hello World From Parent'
});
1_worker.js
//workers inner scope
//invoked when Parent calls worker.postmessage()
self.addEventListener("message", function (event) {
voltmx.print('Worker Scope : onmessage : event.data : ' + event.data["message"]);
//call func
do_something_in_worker();
});
function do_something_in_worker() {
voltmx.print('Worker Scope : invoking postMessage()');
//will invoke Parent worker.onmessage()
postMessage({
'message': "Hello World From Worker "
});
};
Expected Output
"Parent Scope: Invoking worker.postmessage()"
"Worker Scope: onmessage : event.data : " "Hello World From Parent"
"Worker Scope: invoking postMessage()"
"Parent Scope: onmessage : event.data : " "Hello World From Worker "
Explanation
- In Parent Scope: Creates new worker using new voltmx.worker.WorkerThread ()
- In Parent Scope: Call to worker.postMessage() invokes message event handler registered using addEventListener() in worker threads inner scope.
- In Worker Scope: Call to postMessage() invokes message event handler registered using addEventListener() in the parent thread scope.
Nesting of Threads and Performing Parallel Tasks
Main.js
try {
voltmx.print("Parent Scope: Init test_case_parent_thread()");
voltmx.print("Parent Scope: In try block");
//create new voltmx.worker.WorkerThread
var worker = new voltmx.worker.WorkerThread('WorkerThread.js');
//invoked when worker calls postmessage() from its inner scope
worker.addEventListener("message", function (event) {
voltmx.print('Parent Scope : onmessage : event.data : ' + event.data);
});
worker.postMessage("Hello from Parent");
} catch (err) {
voltmx.print("Parent Scope: In Catch block");
}
//invoke a function
invoke_timer_task();
//
function invoke_timer_task() {
voltmx.print("Parent Scope :- voltmx.timer.schedule - ");
var timerId = "mytimer12111";
var i = 0;
function timerFunc() {
i++;
voltmx.print("Parent Scope :- voltmx.timer.schedule - In timerFunc() : " + i);
if (i > 20) {
voltmx.print("Parent Scope :- voltmx.timer.schedule - Stopping timer : ");
voltmx.timer.cancel(timerId);
}
};
//
voltmx.timer.schedule(timerId, timerFunc, 1, true);
voltmx.print("Parent Scope :- voltmx.timer.schedule - Done");
};
voltmx.print("Parent Scope: Exit test_case_parent_thread()");
WorkerThread.js
//worker
//workers inner scope
voltmx.print("Worker Scope: Init");
var worker = new voltmx.worker.WorkerThread('WorkerThread2.js');
//invoked when Parent calls worker.postmessage()
this.addEventListener("message", function(event) {
voltmx.print('Worker Scope : onmessage : event.data : ' + event.data);
});
self.postMessage("Hello from Worker");
//
invoke_timer_task();
//
function i