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.
1. 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 Visualizer, the Kony HttpRequest and DOMParser related code are added to the AWS files. These modified files are then saved in the cloudsdks folder by Visualizer.
2. 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 |
|---|---|
kony.worker.hasWorkerThreadSupport | Determines whether the current platform environment has worker thread support. |
kony.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. |
SPA
| iOS | Blackberry OS | Android Native | Android Chrome | Windows phone OS |
|---|---|---|---|---|
5.0-5.1 | 10.0 | 4.4 | 33.0 | 8 |
DesktopWeb
| IE | Firefox | Chrome | Safari |
|---|---|---|---|
| 10.0 | 4.0 | 20 | 5.0 |
Windows:
Windows Phone 8
Windows Phone 8.1
Windows Kiosk
Windows 8.1
workerthreads directory in modules/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.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 kony.worker.WorkerThread('helper.js');
var worker = new kony.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) { ... });
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 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.
function(event) { });
String / JSON Object
var evtMessageHandler_1 = function(event) {
//In case of JSONkony.print ("Received message :" + event.data["msg"]);"
//In case of string
kony.print ("Received message :" + event.data);
};
Available for iOS, Android, Windows, SPA, and Desktop Web. For more information, see Scope.
"error" event handler receives an "event" object which has the has the following three fields: message, filename, lineno. Registered ‘error’ event handler is invoked whenever an unhandled exception arises in worker’s scope. "error" event handler can be registered in parent thread scope on worker object and as well as in worker thread’s inner scope, where both event handlers will be invoked if present whenever an unhandled exception occurs in workers inner scope.
Multiple "error" event handlers can also be registered in Parent scope and in workers inner scope and all registered event handlers will be invoked in the registered order whenever an unhandled exception arises in worker’s scope.
function(event) { });
error event object (message, filename, lineno)
function(event) {
kony.print ('ERROR: Line '+ event.lineno + ' in ' + event.filename + ': ' + event.message);
}
Available for iOS, Android, Windows, SPA, and Desktop Web. For more information, see Scope.
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 " kony.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 Kony 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.
importScripts(".js_file_name");
or
importScripts("functional_module_name");
JSFileNames [Object]
or
Functional_Module_Name [Object]
importScripts("Utility.js"); // loads Util.js
importScripts("Utility1.js", "Utility2.js", "Utility3.js");
None
Note: If no argument is given, no exception is raised and it does nothing.
When an error is encountered, the KonyError 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. <scriptname> | 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 kony.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. |
| kony.modules.loadFunctionalModule() function cannot be used in workers scope to load any FunctionalModule. | kony.modules.loadFunctionalModule() function can be used in workers scope to load any JavaScript script which is part of some Functional Module. |
Available for iOS, Android, Windows, SPA, and Desktop Web. For more information, see Scope.
The following topics helps you to use the worker thread feature:
Main.js
//create new worker
var worker = new kony.worker.WorkerThread('1_worker.js');
//invoked when worker calls postmessage() from its inner scope
worker.addEventListener("message", function (event) {
kony.print('Parent Scope : onmessage : event.data : ' + event.data["message"]);
});
kony.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) {
kony.print('Worker Scope : onmessage : event.data : ' + event.data["message"]);
//call func
do_something_in_worker();
});
function do_something_in_worker() {
kony.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
Main.js
try {
kony.print("Parent Scope: Init test_case_parent_thread()");
kony.print("Parent Scope: In try block");
//create new kony.worker.WorkerThread
var worker = new kony.worker.WorkerThread('WorkerThread.js');
//invoked when worker calls postmessage() from its inner scope
worker.addEventListener("message", function (event) {
kony.print('Parent Scope : onmessage : event.data : ' + event.data);
});
worker.postMessage("Hello from Parent");
} catch (err) {
kony.print("Parent Scope: In Catch block");
}
//invoke a function
invoke_timer_task();
//
function invoke_timer_task() {
kony.print("Parent Scope :- kony.timer.schedule - ");
var timerId = "mytimer12111";
var i = 0;
function timerFunc() {
i++;
kony.print("Parent Scope :- kony.timer.schedule - In timerFunc() : " + i);
if (i > 20) {
kony.print("Parent Scope :- kony.timer.schedule - Stopping timer : ");
kony.timer.cancel(timerId);
}
};
//
kony.timer.schedule(timerId, timerFunc, 1, true);
kony.print("Parent Scope :- kony.timer.schedule - Done");
};
kony.print("Parent Scope: Exit test_case_parent_thread()");WorkerThread.js
//worker
//workers inner scope
kony.print("Worker Scope: Init");
var worker = new kony.worker.WorkerThread('WorkerThread2.js');
//invoked when Parent calls worker.postmessage()
this.addEventListener("message", function(event) {
kony.print('Worker Scope : onmessage : event.data : ' + event.data);
});
self.postMessage("Hello from Worker");
//
invoke_timer_task();
//
function invoke_timer_task() {
kony.print("Worker Scope :- kony.timer.schedule - ");
var timerId = "mytimer121";
var i = 0;
function timerFunc() {
i++;
kony.print("Worker Scope :- kony.timer.schedule - In timerFunc() : " + i);
if(i > 20) {
kony.print("Worker Scope :- kony.timer.schedule - Stopping timer : ");
kony.timer.cancel(timerId);
}
};
//
kony.timer.schedule(timerId,timerFunc, 1, true);
kony.print("Worker Scope :- kony.timer.schedule - Done");
};
kony.print("Worker Scope: Loading done");
WorkerThread2.js
//Grand child worker2 – nested worker
//workers inner scope
kony.print("Grand child: Worker2 Scope: Init");
//invoked when Parent calls worker.postmessage()
this.addEventListener("message", function(event) {
kony.print('Grand child: Worker2 Scope : onmessage : event.data : ' + event.data);
});
self.postMessage("Hello from Worker2");
//
invoke_timer_task();
//
function invoke_timer_task () {
kony.print("Grand child: Worker2 Scope :- kony.timer.schedule - ");
var timerId = "mytimer1211";
var i = 0;
function timerFunc()
{
i++;
kony.print("Worker2 Scope :- kony.timer.schedule - In timerFunc() : " + i + " : Grand child ");
if(i > 20) {
kony.print("Grand child: Worker2 Scope :- kony.timer.schedule - Stopping timer : ");
kony.timer.cancel(timerId);
}
};
//
kony.timer.schedule(timerId,timerFunc, 1, true);
kony.print("Grand child: Worker2 Scope :- kony.timer.schedule - Done");
};
kony.print("Grand child: Worker2 Scope : Loading done");
Global resources in the App context will not be available in the worker thread context as it can lead to Race conditions since no locking mechanisms are provided.
Every worker has its own context of execution, which is not shared between the parent and its worker. As a result the global variables in parent scope are not available in worker scope and vice versa.
| Not Supported APIs |
| |||||||||||||||||||||||||||||||||||||||||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| Supported APIs and Platform |
| |||||||||||||||||||||||||||||||||||||||||||||||||||||
With and without Functional Module in Worker context:
| iOS | Android | Windows | SPA | |
|---|---|---|---|---|
| FFI | Platform will load modules by default | |||
| Custom Widgets | No need to load Custom Widgets in worker scope. | |||
The following guidelines are recommended before using worker thread:
As explicit thread synchronization mechanisms like locking or mutexes are not available in JS environment, you must take required care in scenarios where multiple threads concurrently or simultaneously are trying to access and write/insert data into local database or local datastore using WebSQL or Local datastore APIs, as these are shared resources across the Application context.
Limitations in SPA and DesktopWeb:
Note: For SPA and Desktop Web, nested workers are not supported in Google Chrome.
terminate API
As the worker threading model is mapped to underlying native threading models in native platforms, there can be some deviations from what specification says, this is due to technical limitations in the underlying platforms which include:
close API
Debugger support for worker threads is not available in 5.6.2 release.
Following are some worker life cycle notes:

| Copyright © 2013 Kony, Inc. All rights reserved. |