I recently started building a demo to highlight application design practices that leverage HTML5 features to deliver an “app” experience. The model for this particular demo was that of a video portal and the first step was to to make independent XmlHttpRequest calls to fetch the videos on demand.

Here is where we want to ed up with this app.

  1. The content gets updated with LINKS to new videos.
  2. The user selects videos to be downloaded.
  3. The videos are downloaded in the background without effecting the user interface performance.
  4. The user can select as many videos as they want for download and they will be queued up.
  5. The videos will be stored locally for use later.
  6. The user will be warned if they try to close the app when files are still downloading.

I posted a simple demo of using Web Workers here.

In this post we’ll look at how to stack all of the user’s video download requests in a JavaScript queue object. This is necessary because the user can click on links to videos faster than they can be downloaded. In our previous example if the user clicks on a video to download and then clicks on a different video before the first one has finished downloading – that first video download is simply abandoned and the second video download begins. We need to queue them so that all the videos selected will be asynchronously downloaded in the background.

Lets first look at a simple JavaScript Queue – then we will look at the better way to do solve the problem.

In JavaScript the array is a pretty flexible construct.

To create an instance of an array :


var queue = new Array();

An array in JavaScript can contain any type of data (more on this in a minute) and each item in an array does not have to be of the same type as the others.

There are two sets of ways to add and remove items from an array.

One set works against the beginning of an array and the other works against the bottom.


shift() – Removes the first element of an array, and returns that element
unshift() – Adds new elements to the beginning of an array, and returns the new length


pop() – Removes the last element of an array, and returns that element
push() – Adds new elements to the end of an array, and returns the new length


In order to process a FiFo (First In – First Out) queue, we’ll need to use one of each.

We’ll use push() to add an additional item to the top of the queue and we’ll use shift() to retrieve (and remove)  an item from the bottom of the queue and move all the remaining items down to fill in for the removed item.

Given the following HTML :



<!doctype html>  
<html>  
  <head>  
    <meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />  
    <script src="js/code.js"></script>
    <script type="text/javascript">

      function doStackAdd() {
        var inputValue = document.getElementById('txtInput').value;
        if(inputValue) {
          stackAdd(inputValue);
          document.getElementById('txtInput').value = "";
        }
      }
		
      function doStackGet() {
        var returnValue = stackGet();
        document.getElementById('stackResult').innerHTML = returnValue;
      }
    </script>   
    <title>JavaScript FIFO - Worker</title>  
  </head>  
  <body>
    <center>
      <h3>JavaScript Simple FiFo Queue</h3>
      <div id="mainDiv" style="background-color: silver; 
                        width:410px; padding: 20px; text-align: left;">
        Input : <input type="text" id="txtInput" value="">
        <button onclick="doStackAdd()">Add to Stack</button> 
        <br /><br />
        <button onclick="doStackGet()">Get from Stack</button> >>>
        &nbsp;&nbsp;
        <span id="stackResult"></span>
      </div>	           
     </center>
   </body>  
</html>

Which renders the following page…


Note that the button click() event handlers call JavaScript functions defined in the code.js file.


//---------------------------------------------------------------------+
// Code for SIMPLE FiFo Queue (Stack)
//---------------------------------------------------------------------+
var queue = new Array();

function stackAdd(valueToAdd) {

	queue.push(valueToAdd);
}

function stackGet() {
	var retrievedQueueValue = queue.shift();
	if(retrievedQueueValue) {
	   return retrievedQueueValue;
    }
    else {
	   return "Queue is empty.";
	}
}

So, when we add a few items to the queue by entering text and clicking the “add” button…….





We then have a few (three) items on the stack.

Now if we start clicking on he “Get from Stack” button we can see that the items we entered come off the stack in the order we put them on.





When the queue is empty, we report that the queue is empty.



This works, but only allows us to push “single” values onto the stack.

Remember that our use-case for this is to put videos that we want to download into a queue.

Practically speaking, we will want to attach some information to the video to be downloaded. AT a minimum a name for the video, maybe a description.

At first you may consider a JavaScript Hash, also known as an associative array.

But this data structure is not very well suited for this type of usage.

With an Associative Array you store Key / Value Pairs. The key is not really part of the data, it’s the index into that element of an array. As such you use the value of the key to retrieve the item you want.

It makes more sense for us to define a custom type ( a video for download type) and then work with an array of object of that type.

So for the given markup….



<!doctype html>  
<html>  
  <head>  
    <meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />  
    <script src="js/code.js"></script>
    <script type="text/javascript">
      
      function doObjArrayAdd() {
        var inputName = document.getElementById('txtInputName').value;
        var inputURL = document.getElementById('txtInputURL').value;
        var inputDesc = document.getElementById('txtInputDesc').value;	
 
        if(inputName && inputURL && inputDesc) {
          objArrayAdd(inputName, inputURL, inputDesc);
          document.getElementById('txtInputName').value = "";
          document.getElementById('txtInputURL').value = "";
          document.getElementById('txtInputDesc').value = "";
         }
       }

      function doObjArrayGet() {
        var returnValue = objArrayGet();
        document.getElementById('vidoObjectResult').innerHTML = returnValue;
      }
    </script>   
    <title>JavaScript FIFO - Worker</title>  
  </head>  
  <body>
    <center>
      <h3>JavaScript FiFo Queue - Object Array</h3>
      <div id="mainDiv" style="background-color: silver; 
                        width:500px; padding: 20px; text-align: left;">
        Name : 
        <input type="text" id="txtInputName" value=""  style="width:300px;">
        <br />
        &nbsp;&nbsp;&nbsp;URL : 
        <input type="text" id="txtInputURL" value="" style="width:300px;">
        <br /><br />
        Desctiption :  
        <textarea id="txtInputDesc" rows="8" cols="68"></textarea> 
        <button onclick="doObjArrayAdd()">Add to Collection</button> 
        <br /><br />
        <button onclick="doObjArrayGet()">Get from Collection</button>
        <br /><br />
        <span id="vidoObjectResult">&nbsp;</span>
      </div>		           
     </center>
   </body>  
</html>

Then in our button click() event handler we will define our custom type and create an array of object instances from our user defined type.


//---------------------------------------------------------------------+
// Code for Object  FiFo Queue 
//---------------------------------------------------------------------+
function VideoObject(name, url, desc)
{
this.name = name;
this.url = url;
this.desc = desc;
} 

var VideoObjectArray = new Array();

function objArrayAdd(nameToAdd, urlToAdd, descToAdd) {
	
   newVideo = new VideoObject(nameToAdd, urlToAdd, descToAdd);  
   VideoObjectArray.push(newVideo);
}

function objArrayGet() {
   var retrievedVideoObject = new VideoObject();
   retrievedVideoObject = VideoObjectArray.shift(); 
	
   if(retrievedVideoObject) {
      return retrievedVideoObject.name + ":" + 
             retrievedVideoObject.url + ":" + retrievedVideoObject.desc;
   }
   else {
      return "VideoObjectArray is empty.";
   }
}

Note that when we use shift() to retrieve an item from our array we can’t don’t pass that object instance to the calling method. It would work here in this example, but when we move this code to a Web Worker, this code will be called via a Web Worker message and the calling code will not have access to the type defined in the referenced .js file.

Now when we run the page and add an item to the queue…..



And then click on the button to retrieve the object….



So there we have an implemented FiFo Object Queue in JavaScript.

What’s next ?

  • Store the videos locally.
  • Implement AppCache to work off line
  • Move the downloading into a web worker
  • Update the UI to destinguish between, available, downloading, and downloaded videos
  • Prevent the user from closing the app while files are still downloading.


** You can download the source code for the above example HERE.