Multithreading in JavaScript

an article added by: Sonja Lande at 06012007


In: Categories » » AJAX » Multithreading in JavaScript

Mimicking Multithreading in JavaScript

A proactive event-driven architecture is like the dog asking (if it could), “When are you going to throw the ball, when, huh, when?” The pseudo-thread architecture is implemented using timeouts. The pseudo-thread is the result of a periodic function or object call, and the complete implementation is shown in the following source code.

   Source: /client/scripts/jaxson/common.js
   function ThreadObject() {
   this.obj = null;
   this.data = null;
   this.intervalId = null;
   this.isUsed = false;
   }
   ThreadObject.prototype.makeCall = function() {
   if( typeof( this.obj) == "function")  {
   this.obj( this.data);
   }
   else if( typeof( this.obj) ==  "object") {
   this.obj.run( this.data);
   }
   }
   var Thread = {
   threadObjects : new Array(),
   startThread : function( obj, data, time) {
   for( var c1 = 0; c1 <  this.threadObjects.length; c1 ++) {
   if( !this.threadObjects[ c1].isUsed) {
   this.threadObjects[ c1].isUsed = true;
   this.threadObjects[ c1].data = data;
   this.threadObjects[ c1].obj = obj;
   this.threadObjects[ c1].intervalId =
   window.setInterval( "Thread.threadObjects[  " + c1 + "].
   makeCall()", time);
   return this.threadObjects[ c1].intervalId;
   }
   }
   throw new Error( "Could not start a  thread");
   },
   endThread : function( intervalId) {
   for( var c1 = 0; c1 <  this.threadObjects.length; c1 ++) {
   if( this.threadObjects[ c1].intervalId ==  intervalId) {
   window.clearInterval( intervalId);
   this.threadObjects[ c1].isUsed = false;
   }
   }
   }
   }
   function InitializeThreads( maxThreads) {
   for( var c1 = 0; c1 < maxThreads; c1 ++) {
   Thread.threadObjects.push( new ThreadObject());
   }
   }
   InitializeThreads( 30);

In the source, two object types are defined: ThreadObject and Thread. The ThreadObject type is used to manage the mock thread, and Thread manages the mock threads. The InitializeThreads function is used to set up the pseudo-threading environment. To explain this source code, a top-down approach is used.

When the JavaScript file containing the pseudo-thread architecture is loaded, the InitializeThreads function is called with a parameter value of 30. InitializeThreads is used to initialize the array of pseudo-threads that will be called. Hard-coding to a limit of 30 threads may seem like a bad programming practice, but there is a reason for it. To create a pseudo-thread, you create a periodic event, which means you need to use either the window.setInterval or window.setTimeout method. Calling either of these methods requires a function reference or a piece of JavaScript code. The problem with both approaches is that you need to serialize the source code. For example, imagine that you want to call either of the periodic event methods with a local object reference inst, as illustrated by the following source code:

   window.setInterval( "CallFunction(  inst)", 300);

The reference to inst in the string buffer will have no meaning, because when the periodic timer executes, local variables are not in context. The execution will result in an error. Another way to call the function is to serialize inst to a buffer, but that has the disadvantage that the state being used in the periodic event will not be the same object instance, thus if any changes are made between the calling of the function setInterval and expiration of the timer, they will not be present. The only time when inst is valid is if inst is a global variable. As a general rule of thumb, the safest way to make a context-specific method call using the methods setInterval or setTimeout is to use a global reference. A global reference can be created by dynamically instantiating random variables or by using an array index reference. The approach chosen by Thread is the array index. The InitializeThreads function will initialize 30 global references of ThreadObject. Each ThreadObject has four data members:

• obj: References the object to execute. The ThreadObject.makeCall method can distinguish between a function and object. If obj is a function, then obj will be called using a function notation. If obj is an object instance, then the obj.run method is called.

• data: References the data used as a context when obj is executed.

• intervalId: References the value returned by the window.setInterval method. This value is used to stop the periodic event.

• isUsed: Used by Thread to determine whether an array index is currently used. When a new thread wants to start, the startThread method searches for an empty index, where isUsed equals a value of false. Once a thread has been started, the isUsed data member is assigned a value of true. And when the thread has finished executing, the isUsed data member is assigned a value of false again. The pseudo-thread architecture is used in the declaration of DynamicIterator referenced by the metadata navigational onmouseover and onmouseout events. When you move the mouse from one region of the HTML page to another, and the other region has the onmouseover event implemented, the event is triggered. The onmouseover event is triggered only the first time the mouse moves over the event, and that causes the startIteration method to be called, calling Thread.startThread. When the mouse moves out of the metadata navigation region, the stopIteration method is called, calling Thread.endThread. The implementation of startIteration and stopIteration is defined as follows.

   Source: /client/scripts/jaxson/uimorphing.js (DynamicIterator)
   intervalId : 0,
   startIteration : function(direction) {
   this.intervalId = Thread.startThread( function(  direc) {
   DynamicIterator.shiftArrayElements( direc);
   }, direction, 500);
   },
   stopIteration : function() {
   Thread.endThread( this.intervalId);
   },

Putting the Remaining Pieces of the Client Together

Several times I’ve referenced the DynamicIterator implementation, which is used to navigate the metadata of the result set. The main role of DynamicIterator is to load the metadata navigational elements, and when the individual elements are referenced, the main data is loaded. I explain the implementation of DynamicIterator in pieces, with the following representation of the initialization of the navigation elements.

   Source: /client/scripts/jaxson/uimorphing.js
   var DynamicIterator = {
   lastElem : null,
   floatingIframe : null,
   parentRow : null,
   initialize : function(floatingIframeID,  parentRowID) {
   this.floatingIframe =  document.getElementById(floatingIframeID);
   this.parentRow =  document.getElementById(parentRowID);
   this.doLayout();
   this.getMoreRootElements( 0);
   },
   doLayout : function() {
   this.floatingIframe.style.width =  document.body.clientWidth - 4;
   this.floatingIframe.style.height =  document.body.clientHeight - 104;
   },
   // Other declarations...
   };

 

Going back to the beginning of the “Implementing the HTML Client” section, recall the piece of HTML code that implemented the body.onload event, which called the local Initialize function. In the implementation of Initialize, the DynamicIterator.initialize method is called. Calling DynamicIterator.initialize will cross-reference the HTML user interface elements with the DynamicIterator instance.

DynamicIterator.initialize expects two HTML user interface elements: the floating iframe (this.floatingIframe) and the table row (this.parentRow) that contains the navigational elements. DynamicIterator needs these two user interface elements because it loads the data highlighted in the navigational area. After the user interface elements have been assigned to the data members, the doLayout method is called. The purpose of doLayout is to resize the iframe so that it covers the appropriate client area. And the last method call in DynamicIterator.initialize is the calling of the getMoreRootElements method, which is responsible for loading the metadata navigational elements. In the context of DynamicIterator, the getMoreRootElements method is declared as an empty function similar to the following:

   getMoreRootElements : function( direction) { }

The empty function is a placeholder, and it is expected that the HTML code will declare an implementation. In the case of the HTML code, getMoreRootElements is defined as follows.

   Source: /client/ajaxrest articles/architecture/dynamiclist.html
   DynamicIterator.getMoreRootElements = function(  direction) {
   if( direction == 0) {
   var asynchronous =  FactoryHttp.getCachedAsynchronous();
   asynchronous.settings = {
   onComplete : function(xmlhttp) {
   var arrTickers = new Array();
   var tickers = JSON.parse(  xmlhttp.responseText);
   for( var c1 = 0; c1 < tickers.length; c1 ++)  {
   arrTickers.push( { text : tickers[ c1],
   url :  "/pyservices/trader/historical/tickers/" +
   tickers[ c1]});
   }
   DynamicIterator.associateElements( arrTickers);
   }
   }
   asynchronous.get("/pyservices/trader/historical/tickers");
   }
   }

In the case of the HTML code, the tickers that represent the metadata used to navigate through the application are loaded once. Metadata might be unlimited, but often it can be limited to a fixed set size, even if that set size is very large. Looking at the example, each piece of metadata is a ticker that can have one to four letters (on average). If you multiply that number by 1,000 tickers, then you have to download about 4KB–5KB of data (in this era of broadband, downloading 4KB–5KB is trivial). The stock application will allow at most 50 tickers due to technical limitations, so all of the tickers can be downloaded in one request.

In the case of our example, getMoreRootElements with a direction of 0 means to download an initial set of metadata elements. How that data is downloaded is the responsibility of the getMoreRootElements implementation. This example uses the Asynchronous class, and the response is encoded as a JSON array. For every ticker found, an object is created where the ticker is combined with a URL and added to the arrTickers array. Once the arrTickers array has been filled with elements, the DynamicIterator. associateElements method is called as follows.

   Source: /client/scripts/jaxson/uimorphing.js (DynamicIterator)
   associateElements : function( arrElements) {
   this.arrElements = arrElements;
   this.shiftArrayElements( 0);
   },

The associateElements method does two things: assigns the arrElements reference array and refreshes the data in the HTML page using the shiftArrayElements method. If the user hovers the mouse over either the left or right arrow, the list of tickers shifts to the left or right.

The index movement is contained within the shiftArrayElements method. The caller of the method provides the direction of the shift using a positive or negative value. Based on the directional value, the index will be shifted. If the shifted index goes beyond the boundaries of the array, then more metadata elements need to be retrieved. The full implementation of shiftArrayElements is as follows.

   Source: /client/scripts/jaxson/uimorphing.js (DynamicIterator)
   shiftArrayElements : function( offset) {
   this.currOffset = this.currOffset + offset;
   if( this.currOffset < 0) {
   this.getMoreRootElements( -1);
   this.currOffset = 0;
   }
   else if((this.currOffset +  this.parentRow.cells.length) >=
   this.arrElements.length) {
   this.getMoreRootElements( 1);
   this.currOffset =
   this.arrElements.length - this.parentRow.cells.length;
   }
   for( var counter = 1; counter <
   (this.parentRow.cells.length - 1); counter ++)  {
   this.parentRow.cells[ counter].innerHTML =
   this.arrElements[ this.currOffset + counter -  1].text;
   this.parentRow.cells[ counter].refElemInfo =
   this.arrElements[ this.currOffset + counter -  1];
   }
   },

In the implementation of shiftArrayElements, a calculation of the shift requires a test to determine if the shift will cause a jump beyond the boundaries of the array. If a jump to the left or the right of the array boundaries happens, then the getMoreRootElements method is called, where the direction value –1 or 1 specifies to prefix metadata to the beginning of the array or append data to the end of the array, respectively. If the shift does not cause a jump beyond the boundaries of the array, then the individual HTML user elements are redrawn with new reference information attached to the refElemInfo property. It is not customary to attach your own data to an individual HTML user interface element, but in this case it is absolutely necessary for reasons to do with a user hovering the mouse over the element.

If the user hovers the mouse over a ticker, you want the ticker information to be loaded into the floating iframe window. This is accomplished by the DynamicIterator.HightlightItem method, which is defined as follows.

   Source: /client/scripts/jaxson/uimorphing.js (DynamicIterator)
   highlightItem : function (elem) {
   if (this.lastElem != null) {
   this.shrinkElement( this.lastElem);
   }
   this.expandElement( elem);
   this.lastElem = elem;
   this.fetch( elem.refElemInfo);
   },

highlightItem does nothing more than delegate the actions to the shrinkElement, expandElement, and fetch methods. All of these methods are by default empty functions that the HTML page needs to implement. The idea of highlightItem is to provide a clickless navigation scheme. How you make the navigation function is the responsibility of the HTML page. DynamicIterator’s responsibility is the coordination. So, the first step is to resize back to normal the last element that was highlighted using the shrinkElement method. In the case of the ticker example, it is implemented as follows.

   Source: /client/ajaxrest articles/architecture/dynamiclist.html
   DynamicIterator.shrinkElement = function( elem)  {
   elem.style.fontSize = 12;
   }

The ticker implementation is simple in that it shrinks the font of the table cell back to 12. For amore sophisticated user interface, shrinking the element might have meant closing a menu or a pop-up div element. The next step of highlightItem is to highlight the current element using the expandElement function, which is implemented as follows.

   Source: /client/ajaxrest articles/architecture/dynamiclist.html
   DynamicIterator.expandElement = function( elem)  {
   elem.style.fontSize = 30;
   var asynchronous =  FactoryHttp.getCachedAsynchronous();
   asynchronous.settings = {
   onComplete : function(xmlhttp) {
   }
   }
   asynchronous.get(elem.refElemInfo.url);
   }

The expandElement implementation does two things: increases the size of the font and downloads the data associated with the metadata element. However, in the implementation of onComplete, there is no action, because when you highlight the metadata element, you are probably not interested in displaying the data, but you are interested in caching the data for quick future reference. At this point, you need to make a decision about the mechanism of displaying the data in the iframe. If you want to display the data using a click, then you need to implement onclick for the individual table cells. If you want to use the act of highlighting as the trigger to display the data, then you need to implement the onComplete method that I just said did not need to be implemented. My point is that the way you display the data is up to you, and DynamicIterator defines no framework. DynamicIterator is only responsible for preloading the data. Some readers may think that retrieving data every time a metadata element is highlighted is very expensive, but the cost depends on how you implemented your cache. This means that whenever Asynchronous is referenced, the first thing it does is check the cache for the information. If the information is already available, the onComplete method is called directly. The magic lies in the cache, and if there is no cache, then every time the metadata element is highlighted, the data associated with the resource will be loaded.

Let’s step back and think about the implementation of the client-side architecture and how it solves our problem of large or slow data sets. If we were working with an email application, then the metadata would be the individual email titles, and we would iterate over the titles and highlight them. To check for new emails, we could spin off a thread that would automatically add metadata elements to the end of the arrElements array. As we iterate over the email titles, their content would be downloaded and displayed in the iframe window. Now let’s say that we were calculating all of the prime numbers and wanted to see the results. In this case, the metadata is the data, and like the email application, a thread would be spun off to keep adding results to the arrElements array. I mentioned these two examples of threads being spun off, because thus far the example illustrates only the expansion and loading of data when the user asks for it. Using JavaScript threads, the data is automatically loaded without having the user ask for it.

legal notice

Our website is not responsible for the information contained by this article. Web-articles is a free articles resource.
Suggestion: If you need fresh, daily updated content for your website, feel free to use our service. Click here for more information.

Useful tools and features

Link to this article from your page    Send this article to you or to a friend
If you like this article (tutorial), please link to it from your web page using the information above.

related articles

1. Understanding the Definition and Philosophy of REST
Understanding the Definition and Philosophy of REST REST is a controversial topic among Web service enthusiasts, because it’s considered to stand for the opposite of what Web services and SOA are trying to achieve. The problem with this thinking is that REST is not in contradiction with the abstract definition of SOA and Web services. REST is in contradiction with technologies such as SOAP, WSDL, and WS-* specifications. The following offers a quick definition of REST:...

2. The Easiest Way to Get Started with Ajax and REST
The Easiest Way to Get Started with Ajax and REST Problem You want to know the best way to get started with writing Ajax and REST. Solution When developing an Ajax and REST application, you must decide on the tools and frameworks you’ll use. The choice is simple: Use whatever you’re using today, and write some Ajax applications. You don’t need to change the tools you’re using today. Whether you’re using ASP.NET, JavaServer Pages (JSP), PHP, Ruby, or Python, you...

3. Testing a Dynamic Contract with Ajax
Coding the Contract Using Test-Driven Development Techniques Coding the contract using agile and test-driven development techniques requires writing a number of tests and implementing aMock URL layer. Problem You want to code the contract using these development techniques. Solution To demonstrate, let’s define a use case, implement the use case as a contract, write a test case(s) to implement the contract, implement the contract in the Mock URL, and finally...

4. Testing the Client Side Logic
Problem You want to effectively test your application’s client-side logic. Theory Testing GUI code tends not to be a productive task because of the complications that arise. The main complication is how to test the correctness of a user interface. Imagine a situation where clicking a button causes a table to be filled with data. Now imagine that when a check box is checked and the button is clicked again, a different table is filled with content. The fact that clicking the same button results in two ...

5. Understanding JavaScript and Types
Understanding JavaScript and Types Problem You want to work around the fact that JavaScript does not have types declared for its variables. Theory JavaScript code does not have any variables with a declared type. The lack of typed variables is apparent when you declare functions. That said, not having typed variable declarations does not mean JavaScript has no types or no type safety. Let’s start out with the simple declaration of a function, as illustrated by the following ex...

6. Coding Using Conventions and Not Configurations
Coding Using Conventions and Not Configurations Problem You want to make your JavaScript constructs more efficient by applying the Rails “convention over configuration” principle to them. Theory You may already be familiar with the programming platform Ruby on Rails, which is used to build Web applications. The focus of this recipe is not Ruby on Rails, but one aspect of Ruby on Rails namely, convention over configuration (see http://en.wikipedia.org/wiki/ Ruby_on_Rails for m...

7. Advantage of parameterless functions in JavaScript
Using Parameterless Functions Problem You want to take advantage of parameterless functions in JavaScript. Theory JavaScript functions for the most part have parameters. You may think that the previous sentence states the obvious after all, without parameters, what data could be passed to a function? JavaScript has the ability to declare functions that have no parameters, even though the caller of the function has passed parameters to the function. For example, let’s look at...

8. JavaScripot Functions
Treating Functions Like Objects Problem You want to take advantage of the fact that functions are objects (remember, everything is an object in JavaScript). Theory Many people think that a function is some keyword used in JavaScript. A function is also an object that can be manipulated. Knowing that a function is an object makes it very interesting from the perspective of writing JavaScript code, because the code can treat the function like another other object. This mean...

9. Implementing an Error and Exception Handling Strategy
Implementing an Error and Exception Handling Strategy Problem You want to implement a clean error and exception handling strategy in your applications, to make them run more smoothly. Theory Of course, you might argue that one error is a dialog box and the other is generated in the JavaScript console. The fact that one browser uses a dialog box to show an error and the other does not is a browser issue, not an error issue. A concise way of classifying the two errors is to ...