Back to Blog Home
← all posts

Using Workers in NativeScript to Execute JavaScript on Background Threads

October 19, 2016 — by Ivan Buhov

Introduction

The NativeScript 2.4 release is just around the corner but we can't wait for its official date to spread the great news. Support for JavaScript execution on background thread is one of the most voted features in the NativeScript ideas portal and finally it is here. If you can't wait for NativeScript 2.4 official date to test the workers, you can give it a try by running the latest and greatest NativeScript bits. Workers support is still in experimental state and some limitations exist, therefore your feedback and suggestions are more than welcome. You can share your ideas here for Android and here for iOS.

What are workers?

A worker is, at its core, a script running in the background. Workers run in their own threads, isolated inside a separate global context, allowing you to execute long-running intensive tasks without the negative effects of blocking the fluid UI you've spent so much time building. NativeScript workers implementation is loosely based on W3C Web Workers Specification but it doesn’t entirely conform to it, because a big part of the spec makes sense only in the context of a browser.

How do I use workers?

Now that you know what workers are all about it’s time to show you a brief example:

var worker = new Worker("./workers/myWorker");


The above will set-up a new thread, initialize a new runtime instance and execute the script file. Next, you would want to talk to your worker, or perhaps receive a result from it. Data is sent between the main thread and workers via messages.

worker.postMessage("Hello from main!");
worker.onmessage = function(msg) {
    console.log("Message received from worker thread: " + msg.data);
}

onmessage = function(msg) {
    console.log("Message received from main thread: " + msg.data); // logs: "worker received message: Hello from main!"
    postMessage(" - Hello main! I am worker!");
}


A worker will be alive until it is explicitly terminated from the main thread by calling worker.terminate() or until it closes itself by calling close() inside the worker script. If the worker is not explicitly terminated or closed, the garbage collector will not collect and dispose the worker instance even if it is out of scope.

For more details on workers API and usage visit the NativeScript workers documentation.

Error handling

Unless you are flawless when developing with JavaScript, it is likely that errors will occur inside the worker script. If a thrown error in a background thread is never caught, the worker onerror handler is called as a last resort.

onerror = function(e) {
    console.log("Error thrown and not caught: " + e);
    return true;
}


When returning true inside the error handler it means that you've handled the exception, you know what you are doing and don't want it to notify the main thread about the error. If worker error handler is not provided or it returns false, the main thread is notified about the error by calling the worker instance’s error handler:

worker.onerror = function(e) {
    console.log("Error thrown and not caught in worker thread: " + e);
}

What can I use them for?

While we encourage you to use the new worker functionality, we want to stress that workers are not a solution for every performance issue you might encounter while developing your application. A great use case for workers is performing CPU intensive operations, for example:

  • Processing media (images, audio, video) - check out the workers sample app which processes an image on a background thread while the main thread is animating UI objects without sluggishness.

  • Heavy computation that needs to be performed relatively often. For example, when working with big tables, changing a value in a field can trigger a heavy operation to recalculate the values in the other fields. Workers will not make the heavy operation faster, but it will keep the UI thread responsive while the calculation is happening on a background thread.

  • Transform a blocking API to asynchronous one. For example, the SQLite library exposes only blocking functions and a pretty good example of NativeScript plugin would be to wrap the SQLite library in an asynchronous API which calls the blocking functions on a dedicated worker thread.

  • Check out the community discussion for more ideas!

Keep in mind that worker initialization and messaging infrastructure could add needless overhead if you use them for smaller tasks that don’t take too much time. Initializing a brand new thread and virtual machine for a quick one-time computation not only will not boost your app performance but it will surely have a negative effect on it. Sometimes it is hard to guess if a given operation should be executed in a worker thread. As a rule of thumb, fallback to workers if you already proved that a given task is hurting your UI responsiveness when executed on the main thread.

What technical problems did we encounter

  • Trying to limit the access to UI elements from worker thread - in native mobile development if you attempt to access UI-related API on a background thread, your application will most likely blow up, telling you not to touch the UI. In web, when developing with workers you have no access to the document object, and the window is very limited too, so as to not allow modifying the DOM. Unfortunately, in NativeScript we cannot easily filter UI from non-UI APIs to expose to the global scope of a worker script. What that means, is that you will still have full native API access, but will need to be mindful what and how you work with it, lest your app bursts into flames.

  • Terminating hanged worker threads - according to W3C specification when terminate() is called on a worker instance the execution of the script should be interrupted mid-flow and the thread should close. This allows you to terminate even a worker with while(true) {} loop in its main script. Since interrupting and aborting a script from another thread is a quite tricky and error-prone procedure, we are waiting for the next event loop to execute the termination logic. This means that you should be careful not to run never ending tasks, because this makes the worker non-closable.

What’s next? How to get involved?

Since NativeScript is a community driven project, you can check out the Android issue and iOS issue to view the workers roadmap and share your feedback and suggestions and get involved in the discussion by leaving a comment. Thought of a really cool workers demo app? Make one and don’t hesitate to show it to the community!