Threads in Javascript

Dec 14 2008 Published by Eneko Alonso under uncategorized

Well, let’s make it clear: There is no way to create actual threads in Javascript. Javascript code runs on a single thread inside the browser. This is, only one line of code is running at a time. In fact, until Google’s Chrome, all pages/tabs opened on a browser where running on the same system thread.

So what is this about Javascript threads? Well, maybe we cannot create them, but we can emulate them. Since Javascript is an event driven language, we can use an Object Oriented approach and create an object that stays ‘alive’ with the use of a timer. Now we only need a way to control that object and to interact with it.

Thread Class

In order to accomplish this functionality I created this Mootools-based Thread class:

  1. Thread = new Class({
  2.   ClassName: 'Thread',
  3.   Implements: [Options, Events],
  4.  
  5.   threadId: '',
  6.   timer: null,
  7.   stopped: true,
  8.  
  9.   options: {
  10.     interval: 1000,
  11.     autostart: false
  12.   },
  13.  
  14.   initialize: function(options) {
  15.     ThreadController.add(this);
  16.     this.setOptions(options);
  17.     if (this.options.autostart) this.start();
  18.   },
  19.  
  20.   start: function() {
  21.     this.stopped = false;
  22.     this.__setTimer();
  23.     this.fireEvent('start', [this]);
  24.   },
  25.  
  26.   stop: function() {
  27.     this.stopped = true;
  28.     this.__stopTimer();
  29.     this.fireEvent('stop', [this]);
  30.   },
  31.  
  32.   setInterval: function(time) {
  33.     this.__stopTimer();
  34.     this.options.interval = time;
  35.     this.__setTimer();
  36.   },
  37.  
  38.   // For inheritance
  39.   beforeExecute: function() {
  40.     this.fireEvent('beforeExecute', [this]);
  41.   },
  42.  
  43.   // For inheritance
  44.   execute: function() {
  45.     this.fireEvent('execute', [this]);
  46.   },
  47.  
  48.   // For inheritance
  49.   afterExecute: function() {
  50.     this.fireEvent('afterExecute', [this]);
  51.   },
  52.  
  53.   /* ————— */
  54.  
  55.   __setTimer: function() {
  56.     if (!this.stopped) {
  57.       this.timer = setTimeout(function(){
  58.         this.__execute(this);
  59.       }.bind(this), this.options.interval);
  60.     }
  61.   },
  62.  
  63.   __stopTimer: function() {
  64.     if (this.timer != null) {
  65.       clearTimeout(this.timer);
  66.       this.timer = null;
  67.     }
  68.   },
  69.  
  70.   /* This function is called from a timer which doesn't
  71.       know about 'this' */
  72.   __execute: function(self) {
  73.     self.beforeExecute();
  74.     self.execute();
  75.     self.afterExecute();
  76.     self.__setTimer();
  77.   }
  78.  
  79. });

Thread Controller

To make my life easier and prevent me from having to declare one variable for every thread I use, I have created a ThreadController object to which every thread created will subscribe. This thread controller will allow us to find threads of a specific class and start/stop them or delete them.

  1. ThreadController = {
  2.   threadCount: 0,
  3.   threads: [],
  4.   add: function(thread) {
  5.     this.threads.push(thread);
  6.     this.threadCount++;
  7.     thread.threadId = 'Thread-' + this.threadCount;
  8.   },
  9.   count: function() {
  10.     return this.threads.length;
  11.   },
  12.   indexOf: function(ClassName) {
  13.     for (var i=0, l=this.threads.length; i<l; i++) {
  14.       if (this.threads[i].ClassName == ClassName) return i;
  15.     };
  16.     return -1;
  17.   },
  18.   remove: function(index) {
  19.     if (index < 0 || index > this.threads.length-1) return;
  20.     var thread = this.threads.splice(index, 1);
  21.     thread[0].stop();
  22.     delete thread[0];
  23.   }
  24. }

Usage

In order to use the Thread class you can do it in two different ways.

First, you can instantiate threads and use the onStart, onStop, onBeforeExecute, onExecute and onAfterExecute events.

  1. var producer = new Thread({
  2.   onExecute: function(thread) {
  3.     // Do something
  4.   }
  5. }).start();

Or, you can create a subclass and override the start, stop, beforeExecute, Execute and afterExecute methods. I like this option better since it let’s you create your own thread based Objects that you can instantiate later. If you use this option do not forget to call this.parent().

  1. var Producer = new Class({
  2.   ClassName: 'Producer',
  3.   Extends: Thread,
  4.   execute: function() {
  5.     this.parent();
  6.     // Do something
  7.   }
  8. });

Producer / Consumer demo

A good example of Javascript threads interacting with each other is the classic Producer/Consumer problem. A Producer object produces goods at some speed. A Consumer object consumes goods at its own speed. Depending of the speed of producers and consumers and the number of each, more or less goods will be available.

  1. var Producer = new Class({
  2.   ClassName: 'Producer',
  3.   Extends: Thread,
  4.   execute: function() {
  5.     this.parent();
  6.  
  7.     var item = new Element('div');
  8.     item.set('text', ++count).addClass('item');
  9.     item.fade('hide').inject($('container'));
  10.     new Fx.Morph(item, {
  11.       onComplete: function() {
  12.         item.addClass('ready');
  13.       }
  14.     }).start({
  15.       opacity: 1
  16.     });
  17.   }
  18. });
  19.  
  20. var Consumer = new Class({
  21.   ClassName: 'Consumer',
  22.   Extends: Thread,
  23.   execute: function() {
  24.     this.parent();
  25.  
  26.     var item = $('container').getFirst('.ready');
  27.     if (item && item.hasClass('ready')) {
  28.       item.removeClass('ready');
  29.       new Fx.Morph(item, {
  30.         onComplete: function() {
  31.           item.destroy();
  32.         }
  33.       }).start({
  34.         opacity: 0
  35.       });
  36.     }
  37.   }
  38. });
  39.  
  40. new Producer().start();
  41. new Consumer().start();
  42. new Consumer().start();
  43. new Consumer().start();
  44. new Consumer().start();

As you can see, creating new producers and consumers is very easy and they will live and run on their own. Check out this Producer/Cosumer demo with Javascript Threads.

Producer / Consumer demo

Thread Synchronization

Now, guess what? The fact that all these threads are not actual threads plays in our advantage. Since two of this objects cannot execute code at the same time, there is no risk of garbaging the value of a variable while changing it at the same time. For example, if two threads are updating the same DOM element, they will never conflict or crash. But you may still need critical sections if two threads are manipulating the same DOM object, for example. You can use temporary classes on the elements, but that doesn’t guarantee no conflicts.

On the Producer/Consumer demo I am using the class “ready” on goods elements to indicate which ones are ready to be consumed and are not being consumed yet. But if you look at the code carefully, would this class prevent two consumers from consuming the same element? Not really. The problem is than between the time a consumer finds a ready element and the time when marks it as not ready, it is possible than a second consumer has found the same element.

Get the code

Latest version:
http://enekoalonso.com/svn/research/classes/thread.js

No responses yet