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.
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:

kony.worker Namespace

FunctionDescription
kony.worker.hasWorkerThreadSupportDetermines whether the current platform environment has worker thread support.
kony.worker.WorkerThreadCreates a WorkerThread object and returns a handle to it. The worker object represents a worker thread.

 

WorkerThread Object

MethodDescription
addEventListenerEvent 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.
closeWorker 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.
postMessagepostMessage() sends a JSON object or String message to the Parent/worker's scope by invoking respective registered "message" event handlers.
removeEventListenerremoveEventListener() is used to remove the previously registered message or error event listener that was registered using addEventListener().
terminateWhen 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

  1. Kony Platform version >= 5.6.2.
  2. Support for JavaScript.
  3. Supported mobile Platforms:
    1. iOS
    2. Android
    3. SPA: Supported List of browsers starting from versions.
  4. Supported List of Browsers
  5. SPA

    iOSBlackberry OSAndroid NativeAndroid ChromeWindows phone OS

    5.0-5.1

    10.0

    4.4

    33.0

    8

    DesktopWeb

    IEFirefoxChromeSafari
    10.04.0205.0
     

  6. Windows: 

    1. Windows Phone 8

    2. Windows Phone 8.1

    3. Windows Kiosk

    4. Windows 8.1

Introduction to Constructor - WorkerThread()

Worker Thread Scenarios

The scenarios of using WorkerThread() constructor are as follows:

Worker Thread Life Cycle

The following steps provide the work flow to use worker thread:

  1. 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.

  2. 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.

  3. 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.

  4. 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

Example


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); 
};

Platform Availability

Available for iOS, Android, Windows, SPA, and Desktop Web. For more information, see Scope.


error event handler

"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.

Syntax

function(event) { });

Input Parameters

error event object (message, filename, lineno)

Example


	function(event) {
		kony.print ('ERROR: Line '+ event.lineno + ' in ' + event.filename + ': ' + event.message);
  }

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 " 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.

Syntax

importScripts(".js_file_name");

or

importScripts("functional_module_name");

Input Parameters

JSFileNames [Object]

or

Functional_Module_Name [Object]

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 KonyError JS object is thrown with the following information:

Error CodeNameMessageReason
3002WorkerThreadErrorimportScripts: InvalidParameter. Invalid script nameThis exception occurs when the argument passed is not a string.
3002WorkerThreadErrorimportScripts: InvalidParameter. Unable to import script. <scriptname> This exception occurs when it is unable to find and load the JS script.

 

Differences in behavior of importScripts() and kony.modules.loadFunctionalModule() API with respect to Functional Modules:

Without Functional ModulesWith 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.

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

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

  1. In Parent Scope: Creates new worker using new kony.worker.WorkerThread ()
  2. In Parent Scope: Call to worker.postMessage() invokes message event handler registered using addEventListener() in worker threads inner scope.
  3. 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 {
    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");

Scope Rules and Supported APIs

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
  • Kony UI APIs and UI object handles
  • App Global variables
Supported APIs and Platform
String APIiOSAndroidWindowsSPADesktop Web
Math APIiOSAndroidWindowsSPADesktop Web
Table APIiOSAndroidWindowsSPADesktop Web
Standard Kony APIiOSAndroidWindows

Limited Availability

* For SPA and Desktop Web, the following APIs are supported:

  • kony.convertToBase64
  • kony.convertToRawBytes
  • kony.type
  • kony.props.getProperty
  • kony.print() is not supported in Safari.
  • Kony.print() is supported in Mozilla from version 29.0.
Network APIiOSAndroidWindows

For SPA and DesktopWeb: 

Limited Availability

  • The following API will not be available in SPA & Desktop Web. kony.net.setNetworkCallbacks
Offline Data Access APILocal StorageiOSAndroidWindows 
Web SQLiOSAndroidWindowsSPA and DesktopWeb
Operating System APIiOSAndroidWindows

* For SPA and Desktop Web, the following APIs are NOT supported:

  • kony.os.addHiddenField
  • kony.os.readHiddenField
  • kony.os.startSecureTransaction
  • kony.os.endSecureTransaction
  • kony.os.getAppContext
  • kony.os.hasAccelerometerSupport
  • kony.os.deviceInfo
  • kony.os.deviceInfo().httpheaders
  • kony.os.addMetaTag
  • kony.os.removeMetaTag
  • kony.os.removeAllMetaTags
  • kony.os.hasGPSSupport
  • kony.os.hasCameraSupport
  • kony.os.hasTouchSupport
  • kony.os.hasOrientationSupport
  • kony.os.getDeviceCurrentOrientation
  • kony.os.print
Cryptography APIiOSAndroidWindows

* For SPA and Desktop Web, the following APIs are NOT supported:

  • kony.crypto.saveKey
  • kony.crypto.deleteKey
  • kony.crypto.readKey

FFI and Custom Widgets

With and without Functional Module in Worker context:

 iOSAndroidWindowsSPA
FFIPlatform will load modules by default
Custom WidgetsNo need to load Custom Widgets in worker scope.

 

Guidelines and Limitations

The following guidelines are recommended before using worker thread:

  1. The objects "this" and "self" are available in worker thread inner scope that is referred as worker thread itself.
  2. Event propagation cannot be stopped by using event.stopPropagation() as in HTML specification. Where event.stopPropagation() stops the DOM event to be propagated further by breaking the event chain.
  3. Data passed between the main thread and workers are copied, but not shared. Objects are serialized as they are handed to the worker, and subsequently, de-serialized on the other end. The main thread and worker do not share the same instance. So the end result is that a duplicate is created on each end. HTML5 worker threads support transferable objects that allow transferring the objects from one thread to other without making a copy.
  4. 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.

    • The outcome of these simultaneous or concurrent accesses to database/datastore might push the database/datastore to inconsistent state and is dependent on the individual platforms WebSQL or DataStore implementations. It is always suggested to avoid such scenarios of multiple threads accessing the database/datastore simultaneously/concurrently.

Limitations in SPA and DesktopWeb:

  1. Nested Workers support will be available only if they are supported by the underlying browser platforms.
  2. Error event messages in error event handler sometimes might be appended with extra prefix or suffix string messages from underlying browsers (like Uncaught, Uncaught error etc).
  3. Even though error event handler is invoked in case of unhandled exceptions, the same event messages might also be logged on browser console.
  4. Line number, file name populated in error event object passed to error event handler might be different from user defined files.

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:

  1. If terminate() is invoked in Parent thread on worker, and if worker is currently executing a large task, it might not immediately terminate, it will continue to execute the task to completion and terminate.
  2. If terminate() is invoked in Parent thread on worker, and if there are pending tasks waiting in the message queues for this worker to perform, some platforms might discard all the pending tasks without accepting them for execution and terminate, and on some platform all the pending tasks are executed to completion and then the worker terminates.
  3. It is to be noted that to achieve cross platform consistency, it is always advised that terminate() be invoked on the worker once all the tasks in message queues are completed.

close API

  1. 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:
  2. When close() is invoked in worker scope, and if worker is currently executing a large task, it might not immediately terminate, it will continue to execute the task to completion and then terminate.
  3. When close () is invoked in worker scope, and if there are pending tasks waiting in the message queues for this worker to perform, some platforms might discard all the pending tasks without accepting them for execution and terminate, and on some platform all the pending tasks are executed to completion and then the worker terminates.
  4. Hence it is to be noted that to achieve cross platform consistency, it is always advised that close() be invoked in worker scope once all the tasks in message queues are completed.

Debugger Support

Debugger support for worker threads is not available in 5.6.2 release.

Worker Life Cycle Notes

Following are some worker life cycle notes: 


Copyright © 2013 Kony, Inc. All rights reserved.