Threading… JavaScript?!

Not too long ago, I was at a “nerd conference” where one of the speakers presented Web Workers. The presentation was great! It was certainly well executed. It contained some good information, and, per the applause, it appeared to be well received.

At any of these “nerd cons” that I attend, I take a few notes on the topics that I find interesting. Then, later I investigate them more thoroughly and play around with the items presented. This nerd con was no exception. So, a few days after it had concluded, I grabbed my notes and started to take a peek. When I got to Web Workers and started to play, I found the general idea quite awesome, but it seemed like Web Workers could just be — I don’t know — cooler.

This started me on the path to thinking, “What if instead of creating these routines that I keep seeing in all of these examples, that kind of, just — start, and return some simple data until I stop the process — what if I could, I don’t know, instantiate classes across threads? Or what if I could start an Ajax call on the fly in a different thread? Or what if I could run NoSQL persistence using ku4collections across threads?” Now, to me, those are interesting questions. So, what I did with Web Workers next was prove that I could, in fact, do all of these things! And, in so doing, ku4js-workers and ku4js-reflection were born.

In this post, we are going to take a look at ku4js-workers, and see just how ku4js-workers proves how we can answer all of the above questions affirmatively. It’s not going to be a long post, and it is not going to cover the ins and outs of Web Workers in general. You can find plenty of that on the web already. If you are interested in just learning about Web Workers, I recommend that you start with the W3C spec, but, if you are interested in taking a look at some cool “next level” stuff that came out of the journey that yielded ku4js-workers, keep reading. It’s pretty damn awesome, and it’s a lot of fun! :{)}

Let’s get started!

The W3C Web Workers “…specification defines an API that allows Web application authors to spawn background workers running scripts in parallel to their main page. This allows for thread-like operation with message-passing as the coordination mechanism.” (W3C spec). Basically, what this means is that a Web Worker will open a place for code execution that is not your main place for execution. Why is this important? Well, because code runs like you read a book. You have one brain so you can process one word at a time. You read left-to-right, top-to-bottom (in English) and consume each page you read, one word at a time. So, therefore, you read one book at a time. What Web Workers do, in a sense, is, say, buy you an intern to help you read. :{)}

Say that you have three books that you have to read, but, in the time allotted, only have time to read one. What you could do is get two interns to read the other two books and just notify you of the important information. That is what Web Workers are. They are interns. :{)} They will do all of the reading and processing and then just notify you of the information that you tell them to give you. The problem is that, in keeping with this intern analogy, you can’t really expect the Web Worker to do anything very complex without giving it all kinds of instructions prior to it executing its job. You can’t keep things abstract or make decisions on the fly. Web Workers just don’t have the capacity to work that way.* They need very precise instructions and only perform very specific tasks until they are statically implemented, or trained if you will, to do their task differently.

(*It should be noted that it is not actually the Web Worker that is doing the processing of info. It is the routine that does this. The Web Worker merely starts  up the routine and passes it some data, but for all intensive purposes it can be viewed as though it is the Web Worker that is doing the work. For, if it weren’t for the Web Worker you could not call the routine and expect it to work on a different ‘thread.’)

So, continuing this horrific analogy, we can say that it would be better if we had a worker that simply understood the business rules of our application, or at least had access to them so that we could delegate decision making to our running application, and if our application so chose, it could inform the worker to, not simply execute a static subroutine, but, instead, instantiate new classes on the fly and execute methods with data that was generated during the course of the application’s execution. — In short, that is exactly what ku4js-workers do!

Now this is where the rubber hits the road. Proof time. — Let’s set up some ku4js-workers! The first thing that you will want to do is grab the latest ku4js-workers project from GitHub and open the example project’s index.html file in your favorite browser*

(*At the time of this writing, not all browsers support all HTML5 features equally, and, therefore, do not support all of these examples equally. For example, at the time of this writing Safari does not support IndexedDB and Firefox does not support IndexedDB in a Web Worker thread. Therefore, it is recommended that you run these examples in Chrome as it has the best support. All of that said, know that there are plenty of features that are supported so don’t be too skittish to try some of these examples in dev, test, stage or even prod. Just be responsible, write your unit tests, perform your functional tests, and you should be fine! One other item to note. The console in Chrome is very substandard in its exception messaging for those errors that occur on a separate thread. That said, it is wise to also open Firefox for those features that work in that browser, as Firebug does a very nice job of returning useful exception messages, including ku4exception messages, in the Firebug console window. ) :{)}

ku4WorkerClient

There are two ways to create a new thread with ku4js-workers:

  1. Simply, call: $.ku4WorkerClient.thread()
  2. Use a custom receiver with $.ku4WorkerClient(PATH). More on this later.

First, let’s discuss the “simple” way.

$.ku4WorkerClient.thread()

When you add ku4js to a project, it is assumed that all of the libraries will be in the same directory. For example, a lib directory. ku4js-workers.js and ku4js-workers-thread.js are no exception. If you set up the ku4js libraries this way, threading will just work. Note that ku4js-workers depends on ku4js-kernel and ku4js-data, both of which should also be in the same directory. You can see and example of this in the example project that ships with the ku4js-workers library.

Okay, now that you understand the assets, let’s create a thread. We can just do this in the console window.

var thread = $.ku4workerClient.thread();

Cool right?! — Okay, not really. — Not yet :{)}

Let’s subscribe to the onSuccess and onError listeners of this thread so that we can get the values that it provides after it has finished processing.

thread
    .onSuccess(function(data) {
        console.log("SUCCESS -- ", data);
    })
    .onError(function(data) {
        console.log("ERROR -- ", data);
    });

Okay, great! Now we just need to invoke something, and, if you have worked with Web Workers in the past, this is where it might get a little different.

Because of a vast number of technical reasons that we will not discuss here, any processing that is done in the worker needs to be contained in the worker. That means that you can pass literal values, but any scope that might be needed will be lost in the transfer. That said, what appears to be a common practice is to create static workers that may or may not take literals as arguments, have a subroutine do some work, and return a literal value. This is really cool, of course, but what if we wanted to do something on they fly pursuant to a result in our main process? Say we wanted to create a class and execute one of it’s methods on a different thread — at run time?! Well, with ku4js-workers you can!

$.ku4WorkerClient.thread(), and $.ku4WebClient(PATH), expose an invocation method that allows you to give instructions to a thread at run time on what to construct via reflection after it is spawned.

“Wait — what?!”

Stay with me for a moment.

Using our example already in progress, let’s say that we want to round some number on a different thread. Well, we know that we have a spectacular ku4js-kernel math function that will yield accurate data so we will use it.*

thread.invoke("$.math", [], "round", [4.153, -2]);

And we see in the console that we get 4.15. The correct answer!

(*If you are unfamiliar with the ku4js-kernel $.math.round function. I recommend you read about it. It is cool! In short, it is a function that correctly rounds decimals to the nearest tens exponent. In this example what we are doing is calling $.math.round(4.153, -2) on a different thread.*)

Let’s try something a little more complex. What if we wanted to instantiate a class on the fly that takes some constructor arguments and then call one of its methods? Simple.

$.ku4workerClient.thread()
    .onSuccess(function(data) {
        console.log(data)
    })
    .onError(function(data) {
        console.log(data);
    })
    .invoke("$.money", [145.67, 'B'], "toString");

Here we instantiated a money class with value 145.67 “B” (Bitcoin), if you will. Then, we executed it’s toString method and logged the result: “B145.67” onSuccess, which is the correct value! To do the same thing in the main thread, you would write:

$.money(145.67, 'B').toString()

As you can see, the invoke method of a ku4workerClient takes the following arguments: (Documentation found here)

  1. Class as a string. Example, “$.money” or “new Array”
  2. Constructor arguments as an array
  3. The methods to call on that class.Thiscan be:
    1. A single string for a method that takes no arguments. Example: “toString”
    2. An object for a method that takes arguments where the key is the method name and the value is an array of arguments to pass to the method. Example {“replace”: [“a”, “b”]} if you wanted to call String replace(“a”, “b”).
    3. An array of objects to be called in order, for fluent interfaces. Example: [{onSuccess: “__CALLBACK__”}, {onError: “__CALLBACK__”}]. There are many classes that have onSuccess and onError subscription methods that return ‘this’ so they can be called as a fluent interface .onSuccess(“__CALLBACK__”).onError(“__CALLBACK__”). The preceeding array is how you would pass them to a ku4workerClient.
  4. Thefourthargumentcan be one of to values:
    1. If you are not using an array objects for your methods in argument 3. You will list the argument values here in an array.
    2. If you are using a fluent interface, this argument is a boolean value that tells the thread if this method or set of methods is an asynchronous execution. This tells the worker to wait for the subsequent asynchronous execution to complete before attempting to return a result. This is useful when executing Ajax calls across threads. Something that we will see later.
  5. The final argument is the argument described in 4.2 above, in the event that you are calling a non-fluent interface of an asynchronous  execution. For example, if you were making an Ajax call, but only wanted to set an onSuccess callback.

(Much more can be found on this in the unit tests for the ku4js-workers project)

Knowing this, lets try to make an Ajax call across threads

$.ku4workerClient.thread()
    .onSuccess(function(data) {
        console.log(data)
    })
    .onError(function(data) {
        console.log(data);
    })
    .invoke("$.service", [], [
      {uri:["./"]}, 
      {onSuccess:["__CALLBACK__"]}, 
      {onError:["__CALLBACK__"]},
      "call"], true);

This example uses ku4js-data $.service method to make an Ajax invocation, though you could use whatever means you choose (I recommend ku4js-data $.service :{)}). You can see that we tell the thread that we want to invoke a “$.service” with no constructor arguments, set the URI to the current page. Then, subscribe to the onSuccess and onError methods. Note the “__CALLBACK__” value. This value tells the thread that you want the thread to return the data to you upon success or error. There are much more complex versions of this call that we will look at later. Finally, you express that you want to call this service, passing no arguments, and you want to execute this call asynchronously.*

(*Note that this is one of those examples that may not work depending upon your set up. If you are running these examples from your file system and not a local server, or other server for that matter, you will have issues. There are security issues with making Ajax calls in the file system and Chrome will bark at you if you try, and probably not let you do it. Know that this is not a failure of ku4js-workers, this is a precaution imposed by Chrome. Simply, run this in a local server environment, and all will be fine.)

Okay! Whew! — So, that is $.ku4WorkerClient.thread(), the first of our methods of creating JavaScript threads. I hope that you are saying to yourself, “Holy [expletive] that is frigg’n awesome!” because I know I was! :{)}

Alright, next up the second means of creating a client.

$.ku4WorkerClient(PATH)

The second method of working with threads does require a little more work on the part of the developer, though, really not much. This method requires that the developer create the “receiver” for the ku4workerClient thread requests. It the previous example, $.ku4WorkerClient.thread() simply uses a preconfigured receiver to give the simple and readily available features that it offers. Because we have already looked at $.ku4WorkerClient.thread(), it might be easiest for us to wrap our heads around $.ku4WorkerClient(PATH) by using a previous example.

The PATH that you must provide $.ku4WorkerClient is the path to your “thread.” $.ku4WorkerClient.thread() is really the same as calling $.ku4WorkerClient(“ku4js-workers-thread.js”). If you look in the ku4js-workers-thread.js you will find this

/* This file assumes that it will be placed in the same directory
 * as the files that it depends on:
 *
 * - ku4js-kernel.js
 * - ku4js-data.js
 * - ku4js-reflection.js
 * - ku4js-workers.js
 *
 * In all ku4js-* instances this should be the lib directory of the scripts
 * project. If you are not using the standard ku4* method of scripting
 * you will need to modify this file accordingly.
 */

importScripts("ku4js-kernel.js");
importScripts("ku4js-data.js");
importScripts("ku4js-reflection.js");
importScripts("ku4js-workers.js");

/*== Add additional imports here ==*/

//importScripts("[PATH]");

/*=================================*/

onmessage = function(event) {
    $.ku4WorkerReceiver().execute(event, function(result) { postMessage($.json.serialize(result)); });
};

This file can be used as a template to create other receivers. The only modifications that should be necessary are the addition of further import scripts as may be needed by adding this to the designated area. The idea here is that you will offer any necessary assets to a potential future instance of a ku4WorkerReceiver so that it may instantiate and execute the methods in the imported script.

That said, you could take our first example in the previous section and write it like so:

$.ku4workerClient("./scripts/example/lib/ku4js-workers-thread.js")
  .onSuccess(function(data) { 
    console.log("SUCCESS -- ", data); 
  })
  .onError(function(data) { 
    console.log("ERROR -- ", data); 
  })
  .invoke("$.math", [], "round", [4.153, -2]);

And we can see, in our console, that we get the same result!

What this is offering us, is the ability to create our own receiver. So, if you like, you could copy the ku4js-workers-thread.js file, modify the import scripts where indicated to include your business logic, set the ku4workerClient path, and then make calls to your classes and methods just as we have been ku4js classes and methods in this post!

Okay, I’m not going to go back through all of the examples to prove $.ku4workerClient(PATH), but, please feel free. Also, go ahead and add your own scripts in a receiver file, and give it a shot. We’d love to see what you come up with!

Okay, there is one thing before we wrap up. Let’s look at one final, but powerhouse example. Let’s prove that we can we store data to an IndexedDB, ku4collection NoSQL database across threads?

“Wait — we can do that?!”

With ku4js-workers, amazingly, yes!*

(*Note that due to lack of IndexDB support in various browsers this example currently will only work in Chrome. As browsers continue their support of new features, including IndexDB, we should see this example working in other browsers.)

Okay, to me, this is just too cool. First, let’s prime our NoSQL database with some data on the current thread. If you do not understand what is going on here, I charge you to read a past post on client-side NoSQL databases, and the related posts for more information. Okay, let’s add some people to our local IndexedDB backed ku4collection

$.ku4collection("persons")
 .store($.ku4indexedDbStore())
 .init([
 {
   firstName: "John",
   lastName: "Richards",
   title: "Owner",
   department: "Executive",
   salary: 30000,
   age: 30
 }, 
 {
   firstName: "Erin",
   lastName: "Samualson", 
   title: "Developer",
   department: "IT",
   salary: 80000,
   age: 24
 },
 {
   firstName: "Jennifer",
   lastName: "McMullen", 
   title: "Project Manager",
   department: "IT",
   salary: 60000,
   age: 25
 },
 {
   firstName: "John",
   lastName: "Johnson", 
   title: "Janitor",
   department: "Maintenance",
   salary: 50000,
   age: 64
 }
]).save(function(err, store){ console.log(err); });

Alright, we should see a log of our collection followed momentarily by a log of null. IndexedDB is running asynchronously, so this can take a moment. Once null has logged we can run a find query. This time, though, let’s run this ku4collection method on a different thread!*

$.ku4workerClient("./scripts/example/lib/ku4js-workers-thread.js")
  .onSuccess(function(err, store) { 
    store.read("persons", function(err, collection) { 
      console.log(collection.find({firstName:"John"})); 
    }); 
  })
  .onError(function(err, store) { 
    console.log(err); 
  })
  .invoke("$.ku4indexedDbStore", [], 
    {"read": ["persons", "^(err, collection){ collection.insert({id:1, name:'myName'}).save(function(){ __CALLBACK__; }) }"
  ]}, true);

(*This could be done without us having to physically wait if we pass our method to the save callback. This is a little heavy for this tutorial though. So for this example we just wait) :{)}

Now, look here! This is absolutely sick! We just ran a NoSQL find query on an IndexedDB database on a separate thread. We did not lock our UI thread, we never called a server, and we got back the exact results that we were asking for! — “Holy [expletive]!” :{)}

There are a couple of concepts here that you may cock your head at. Not the least of which are the use of blocks. This concept is taken from the Apple® addition to C that I commonly use in my Objective-C code in iOS. I am not going to cover blocks in this post, except to say they are a way to pass functions to other threads. They are found in ku4js-reflection, and I will post on them at a later date.

So — there you go — ku4js-workers. I hope that you are as taken back as I was. I mean, this is some really cool stuff! I hope you enjoy these concepts and ku4js-workers. I hope that you find them useful for your projects, and if you do, you share them with others.

Please, comment on and share this post with others if you enjoy it; follow @kodmunki on Twitter for regular updates on new developments, deployments, articles, and tools; and check out kodmunki™ on github for cool, managed, and tested kodmunki™ projects.

Thank you for reading, and as always, happy coding :{)}

Advertisements

Software development leaders at the forefront of the latest in technology. Whether implementing updates or integrating with existing technology; developing products that push the bleeding edge of the latest in tech; or developing open-source products, paving the way of future tech, kodmunki™ inspires innovation, elevates quality, and drives value to production. kodmunki™ are experts in web, mobile, and hybrid solutions development; local and distributed team management and collaboration; and fast, quality, successful product delivery, offering R&D, training, consulting, and development services. Contact us at info@kodmunki.com. Let's discuss your vision.

Posted in JavaScript, ku4js-workers

Enjoy the read? -- Let us know.

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s

kodmunki™ Tweets

Enter your email address and receive notifications of new kodmunki™ posts by email.

%d bloggers like this: