BT

Facilitating the Spread of Knowledge and Innovation in Professional Software Development

Write for InfoQ

Topics

Choose your language

InfoQ Homepage Articles Introducing Multithreaded Programming to JavaScript

Introducing Multithreaded Programming to JavaScript

This item in japanese

Bookmarks

While increasingly more websites are fully or partially based on AJAX, it is still difficult to develop complicated AJAX applications. What is the main issue which causes this difficulty in developing AJAX applications? Is it asynchronous communication with the server, or is it GUI programming? Both are routinely performed by desktop window applications -- so why is development of AJAX applications which do the same things particularly difficult?

Difficulty in AJAX Application Development

Let’s consider this issue using a simple example. Suppose that you want to build a tree-structured bulletin board system which loads the data for each article by communicating with the server according to user requests rather than loading all articles at one time from the server. Each article has 4 pieces of information associated with it: a unique ID within the bulletin board system, the name of the person who posted the article, the content of the article, and an array of the IDs of its child articles. To begin with, let’s assume that there is a JavaScript function named getArticle() which is responsible for loading a single article. This function receives the integer ID of the article to be loaded as an argument, and it retrieves the data of the article with that ID from the server. It then returns an object which has the 4 pieces of information contained in that article: id, name, content, and children. An example of this function in use can be written like this:

function ( id ) {
     var a = getArticle(id);
     document.writeln(a.name + "<br>" + a.content);
 } 

As you may notice, calling this function many times with the same article ID, however, requires communications with the server again and again for no good reason. To counter this problem, consider the function getArticleWithCache(), which is a getArticle() with a cache capability. In this example, the data loaded by getArticle() will be simply retained as a global variable:

var cache = {};
 function getArticleWithCache ( id ) {
     if ( !cache[id] ) {
         cache[id] = getArticle(id);
     }
     return cache[id];
 } 

Now the articles that have been read are cached. Now, let’s consider the function backgroundLoad(), which loads the data of all articles based on this mechanism. This function aims to preload all of the child articles in the background while the user is reading a given article. Because the article data is tree structured, a recursive algorithm can easily be written which traverses the tree and allows all articles to be loaded:

function backgroundLoad ( ids ) {
     for ( var i=0; i < ids.length; i++ ) {
         var a = getArticleWithCache(ids[i]);
         backgroundLoad(a.children);
     }
 } 

The backgroundLoad() function receives an array of IDs as an argument and applies our previously-defined getArticleWithCache() to each ID. This allows the data of the article corresponding to each ID to be cached. Then, by recursively calling backgroundLoad() on the IDs of the child articles of the loaded article, the entire article tree is cached.

So far, everything looks good. If you have worked on AJAX application development, however, you should know that this naïve implementation won’t work successfully. The example has been based on the tacit understanding that getArticle() uses synchronous communication. As a general rule, however, JavaScript requires the use of asynchronous communication in communicating with the server because it has only a single thread. In terms of simplicity, processing everything (including GUI events and rendering), on one thread is a good programming model, because it eliminates the need to think about the complicated problems associated with thread synchronization. On the other hand, it presents a significant problem in developing applications –which appear responsive to the user because the single-thread environment cannot respond to users’ mouse clicking and/or key operation when the thread is working on something else (such as the getArticle() call).

What happens if synchronous communication is carried out within this single-threaded environment? Synchronous communication stops the browser’s execution until the communication result is obtained. The thread cannot respond to users while waiting for the communication result because the call from the server has not been completed, and the thread will remain blocked until the call returns. For this reason, it can not respond to users while it is waiting for the server’s response and the browser therefore looks frozen. This also holds true for the execution of getArticleWithCache() and backgroundLoad(), which are based on getArticle(). Because may take a considerable amount of time to download all of the articles, the browser freezing during that time is a serious problem for backgroundLoad() - since the browser is frozen, it is not possible in the first place to achieve the goal of preloading the data in the background while users are reading articles, since the article will be unreadable.

Since the use of synchronous communication creates a significant problem in usability as described above, JavaScript uses asynchronous communication as a general rule. Therefore, let’s rewrite the program above based on asynchronous communication. JavaScript requires asynchronous communications to be written in an event-driven programming style. In most instances, you specify a callback function which is called once the communication response has been received. For example, getArticleWithCache() defined above can be rewritten as:

var cache = {};
 function getArticleWithCache ( id, callback ) {
     if ( !cache[id] ) {
         callback(cache[id]);
     } else {
         getArticle(id, function( a ){
             cache[id] = a;
             callback(a);
         });
     }
 } 

This program also internally calls the getArticle() function. It should be noted, however, that the version of getArticle() which is designed for asynchronous communication expects to receive a function as the second argument. When this version of getArticle() is called, it sends a request to the server, as before, however the function returns immediately without waiting for a response from the server. This means that when the execution is returned to the caller, the server response has not yet been retrieved. This allows the thread to work on other tasks until the server response is obtained and the callback function is called. As soon as this response is received from the server, the callback function specified as the second argument of getArticle() is invoked with the server’s response as an argument. Likewise, getArticleWithCache() has been changed so that it will expect a callback function as the second argument. This callback function will then be called within the callback function that is passed to getArticle() so that it will be executed after the server-communication is finished.

You may think that the above rewriting alone is rather complicated, but the backgroundLoad() function involves even more complicated rewriting. It can be also rewritten to handle a callback function:

function backgroundLoad ( ids, callback ) {
     var i = 0;
     function l ( ) {
         if ( i < ids.length ) {
             getArticleWithCache(ids[i++], function( a ){
                 backgroundLoad(a.children, l);
             });
         } else {
             callback();
         }
     }
     l();
 } 

This rewritten backgroundLoad() function does not look much like our original function, however there is no difference in what they do. This means that both functions receive an array of IDs, call getArticleWithCache() on each element of the array, and recursively apply backgroundLoad() to the resultant child articles. However, it is not easy to recognize even the loop structure for the array, which was represented by a for-statement in the original program. Why are these two sets of functions that do the same thing so totally different from each other?

The difference results from the fact that any function must return immediately after any function that requires server-communication, such as getArticleWithCache(). The callback function that should receive the server response cannot be called unless the original function is no longer executing. For JavaScript, it is not possible to suspend the program in the middle of loops, such as for-statements, and resume it later at the point where execution was suspended; the loop is therefore represented by recursively passing the callback function instead of using a loop syntax. For those who are familiar with Continuation-Passing Style (CPS), this is a manual implementation of CPS. Because no loop syntax can be used, even the simple program described earlier that traverses a tree requires complicated statements. The problem associated with event-driven programs is known as the control flow problem: loop and other control flow statements are likely to be difficult to understand.

There is another problem: if you convert a function which does not use asynchronous communication into a function that uses asynchronous communication, the rewritten function will need to have a new parameter which is a callback functions. This poses a significant problem to existing APIs since our internal changes will not remain internal, but will result in broken APIs and changes by others using our API.

What is the root cause of all of these problems? That’s right. The fact that JavaScript has only one thread causes the problems. Carrying out asynchronous communication on only one thread requires an event-driven program and complicated statements. If another thread could respond to users while the program is waiting for the server response, acrobatics like this would not be required.

Invitation to Multithreaded Programming

Let me talk about Concurrent.Thread, a library that allows JavaScript to use multiple threads, since this greatly eases the difficulty associated with asynchronous communication in the AJAX development mentioned above. This is a free-software library implemented in JavaScript, available under the Mozilla Public License / GNU General Public License. You can download the source code from the website.

Let’s download and use the source code right away. Suppose that you have saved the downloaded source code as a file named Concurrent.Thread.js. Before doing anything else, let’s run the program below, which has a very naïve implementation:

<script type="text/javascript" src="Concurrent.Thread.js"></script>
 <script type="text/javascript">
     Concurrent.Thread.create(function(){
         var i = 0;
         while ( 1 ) {
             document.body.innerHTML += i++ + "<br>";
         }
     });
 </script> 

Executing this program should display numbers starting with 0 in order. Numbers appear one after another, which you can view by scrolling the page. Now, let’s look at the source code in more detail. It uses a simple infinite loop as indicated by while ( 1 ). In ordinary cases, a JavaScript program like this continues to use the one and only thread, causing the browser to look frozen. Naturally, it does not allow you to scroll the screen. Then, why does the above program allow you to scroll? The key is Concurrent.Thread.create() located above while ( 1 ). This is a method provided by the library; it is for creating a new thread. On a new thread, the function passed as the argument is executed. Let me slightly rewrite the program as follows:

<script type="text/javascript" src="Concurrent.Thread.js"></script>
 <script type="text/javascript">
     function f ( i ){
         while ( 1 ) {
             document.body.innerHTML += i++ + "<br>";
         }
     }
     Concurrent.Thread.create(f, 0);
     Concurrent.Thread.create(f, 100000);
 </script> 

In this program we have a new function f(), which shows numbers repeatedly. This is defined at the top, and the create() method is called twice with f() as arguments. The second argument passed to the create() method is passed to f() without modification. Executing this program shows some small numbers starting with 0, which are followed by some large numbers starting with 100,000 and small numbers again that follow up the first series of small numbers. Like this, you can observe that the program shows alternating lines of small numbers and large numbers. This indicates that two threads are running concurrently.

Let me show you another use of Concurrent.Thread. In the above example, the create() method was called to create a thread. It is also possible to create a thread without calling any library APIs at all. For example, the former example can be expressed as:

<script type="text/javascript" src="Concurrent.Thread.js"></script>
 <script type="text/x-script.multithreaded-js">
     var i = 1;
     while ( 1 ) {
         document.body.innerHTML += i++ + "<br>";
     }
 </script> 

Inside the script tag, an infinite loop is written simply in JavaScript. You should take note of the type attribute of the tag: an unfamiliar value (text/x-script.multithreaded-js) is assigned to it. If this attribute is assigned to the script tag, then Concurrent.Thread executes the content of the tag on a new thread. You should remember that, in this case as well, the library body of Concurrent.Thread must be included.

With Concurrent.Thread, it is possible to switch execution context from one thread to another as needed even if you write a long and continuous program. Let me briefly talk about how this behavior is achieved. In short, code conversion is used. Very roughly speaking, the function passed to the create() method is first converted to a character string, which is then rewritten so that it can be executed on a piecemeal basis. Then, the rewritten function is executed little by little on the scheduler. The scheduler is responsible for coordinating multiple threads. In other words, it makes adjustments so that each of the rewritten functions will be evenly executed. Concurrent.Thread actually does not create new threads but simply simulates a multi-threaded environment on the original single thread.

Although the converted functions appear to be running on different threads, there is actually only one thread running everything. Carrying out synchronous communication within the converted functions will still cause the browser to freeze. You may think that our original problem has not been solved at all, however you don’t have to worry. Concurrent.Thread provides a purpose-built communications library which is implemented using the asynchronous JavaScript communication style and which is designed to allow the other threads to work even when a thread is waiting for a response from the server. This communications library is found under the Concurrent.Thread.Http namespace. For example, it is used as follows:

<script type="text/javascript" src="Concurrent.Thread.js"></script>
 <script type="text/x-script.multithreaded-js">
     var req = Concurrent.Thread.Http.get(url, ["Accept", "*"]);
     if (req.status == 200) {
         alert(req.responseText);
     } else {
         alert(req.statusText);
     }
 </script> 

The get() method retrieves the content of the specified URL using HTTP GET, as its name suggests. It takes a target URL as the first argument and an array representing HTTP header fields as the optional second argument. The get() method communicates with the server and returns an XMLHttpRequest object as the return value when it has received the server response. When the get() method returns, the response had been received. It is not necessary to use a callback function to receive the result. Naturally, there is no worry that the browser freezes while the program is waiting a response from the server. In addition, the post() method can be used to send data to the server:

<script type="text/javascript" src="Concurrent.Thread.js"></script>
 <script type="text/x-script.multithreaded-js">
     var req = Concurrent.Thread.Http.post(url, "key1=val1&key2=val2");
     alert(req.statusText);
 </script> 

The post() method takes a destination URL as the first argument and content body to be sent as the second argument. As with the get() method, you can also assign header fields by the optional third argument.

If you implement getArticle() in the first example using this communications library, then you can quickly write getArticleWithCache(), backgroundLoad(), and other functions that use getArticle() using the naïve method shown at the beginning of this article. Even when that version of backgroundLoad() is reading article data, another thread can respond to users as a matter of course, and the browser therefore does not freeze. Now, do you understand how useful it is to use multiple threads in JavaScript?

For More Information

I explained about Concurrent.Thread, a library which allows you to use multiple threads in JavaScript. The explanation in this article is only an introduction. If you want to know more, I recommend you read the tutorial. Providing more information about the use of Concurrent.Thread and listing documents for advanced-level users, the tutorial is the most suitable material to begin with. You are also encouraged to check the Concurrent.Thread website, which provides much more information.

About the Author

Daisuke Maki: After graduating from the Division of Natural Science, the Faculty of Liberal Arts at International Christian University (Bachelor of Liberal Arts), Daisuke Maki majored in information technology at the graduate school at the University of Electro-Communications. Specializing in Web development and in particular AJAX using JavaScript, he developed Concurrent.Thread. This project was adopted by the Explatory Software Project conducted by Information-Technology Promotion Agency Japan (IPA) in fiscal 2006.

He is currently enrolled in a Ph.D. course at the graduate school of the University of Electro-Communications. He also holds a master’s degree in engineering.

Rate this Article

Adoption
Style

Hello stranger!

You need to Register an InfoQ account or or login to post comments. But there's so much more behind being registered.

Get the most out of the InfoQ experience.

Allowed html: a,b,br,blockquote,i,li,pre,u,ul,p

Community comments

  • The tutorial

    by November Salazar,

    Your message is awaiting moderation. Thank you for participating in the discussion.

    is not available in English.

  • Re: The tutorial

    by Jon Gorrono,

    Your message is awaiting moderation. Thank you for participating in the discussion.

    There is a link to a pdf in English:

    jsthread.sourceforge.net/cgi-bin/wiki/wiki.cgi?...

  • Huge js file

    by Sarath P,

    Your message is awaiting moderation. Thank you for participating in the discussion.

    The file itself is 499 kb - Which is large for javascript files. Even a YUI Compressed file is 289. The SVN has many modules under it, I am not sure if all modules are loaded in the *full* version. But If there is an on-demand *loader* module like YUI et all, it would be good.

  • Re: Huge js file

    by Luciano Deriu,

    Your message is awaiting moderation. Thank you for participating in the discussion.

    Indeed the file is 500KB! which is huge. However that's not as bad as it sounds... if you Minify the JS file like you suggest you get it down to about 290KB. Now place that on your server and add compression and test it using this site...

    www.gidnetwork.com/tools/gzip-test.php



    On my simple Apache server with the mod_deflate enabled the actually KB transferred is only 46KB!


    This roughly equates to having a Flash object on your page... I can see when making real web applications having multi threading JS will help a lot. I haven't yet tested Concurrent, largely because of its initial 500kb file size but now after testing out the "real world" impact i feel its not much of an issue(assuming it does what it says on the tin).

  • Fascinating!

    by Frederick Polgardy,

    Your message is awaiting moderation. Thank you for participating in the discussion.

    This is a fascinating project. I'd be interested to see the code transformation happening in a pre-processing phase as well, if desired, to alleviate the browser from having to essentially implement a JavaScript compiler/code-emitter in JavaScript. Is it possible to generate transformed code with a standalone JavaScript interpreter?

  • Error in code sample?

    by Inderjit Gill,

    Your message is awaiting moderation. Thank you for participating in the discussion.

    The 4th code sample seems to have an incorrect exclamation character in the 'if' statement. Instead it should be:


    var cache = {};
    function getArticleWithCache ( id, callback ) {
    if ( cache[id] ) {
    callback(cache[id]);
    } else {
    getArticle(id, function( a ){
    cache[id] = a;
    callback(a);
    });
    }
    }

  • script file

    by dma dma,

    Your message is awaiting moderation. Thank you for participating in the discussion.

    hey guys does anyone knows where i can get this script(Concurrent.Thread.js)?

    the url does not work

    thanks

  • Timer functionality in javascript.

    by Andrei Sedoi,

    Your message is awaiting moderation. Thank you for participating in the discussion.

    I am currently dealing with the problem of measuring equal intervals of time using pure javascript. It will be great if this framework can address this issue.

  • Executing function on existing thread.

    by Andrei Sedoi,

    Your message is awaiting moderation. Thank you for participating in the discussion.

    Hi Daisuke,

    Could you please show me how can I execute some function on existing thread?

  • Is the function compilation recursive?

    by Nick P,

    Your message is awaiting moderation. Thank you for participating in the discussion.

    Based on some testing, I noticed that if function A and function B are run in two separate threads (using Concurrent.Thread.create), then each function's body is broken up and executed piecemeal (with a chance to yield other threads after each statement) - thereby simulating true concurrency.

    However, if function A calls function C, I don't think function C gets compiled. So if function C is long-running, then it has no chance to yield.

    Is this assessment valid or is there a way to address this?

Allowed html: a,b,br,blockquote,i,li,pre,u,ul,p

Allowed html: a,b,br,blockquote,i,li,pre,u,ul,p

BT