Distributed Image Loading for Slow Connections.

Introduction.

The Internet connection that I have here in Arusha, Tanzania, is not exactly what you could call broadband. This makes me very sensitive about the time it takes for some of my pages to load. In particular I've always been bothered about the time it took the page for my wife's guest house to load. On the main page there are three picture galleries that the user can navigate, a total of 28 600x450 jpegs. I used to display an apologetic message for users with slow connections like mine, which dissappeared when loading was complete.

This was not very satisfactory, so I decided to try a different approach, whereby the first image in each gallery was loaded normally, but the subsequent images were loaded after the initial window load was complete.

The general context

Each gallery has jpeg files with names that have a different prefix. Initially I load ap1.jpg, rs1.jpg, and ms1.jpg. The rest - ap2.jpg through ap8.jpg and so on are subject to delayed loading. The hopeful expectation is that if a user is going to navigate the galleries, they will do so sequentially to start with. So as soon as the document is ready, I want to start loading the remaining images in numerical order.

I fumbled around for a while trying to find a sensible way to do this using jQuery, and eventually settled for the following, once again using nested functions:


function loadImages(prefix, limit)
{
   // The delayed loading is to start at the second image for each gallery,
   // it should proceed until it hits limit.
   var ci = 2;
   var t;

   function startNext()
   {
      // Create an image element, initiate its loading, and set a function
      // to be called on completion.
      t = $('<img >').attr('src', prefix+ci+'.jpg').load(loadIt);
   }

   function loadIt()
   {
      // Load the completed image into a DIV with a corresponding ID
      $('#'+prefix+'-img-'+ci).append(t);

      // Check if there are more images to load.
      ci++;
      if (ci < limit)
         startNext();
   }

   startNext();
}
When the function is entered, ci is initialized, and startNext() is called. This function creates an image element, sets its source attribute to the constructed file name, and then specifies the function to be called when loading is complete.

The specified function - loadIt() inserts the completed image element into the DIV with a constructed ID, increments the current image number, then calls startNext() again if the limit has not been reached.

This seems to me to be a good way in general to deal with sequences of operations that might involve delay. If you find yourself tempted to use setTimeout() and clearTimeout() to implement things of that sort, try rearranging the problem along these lines.

At document-ready, we then specify that the loadImages() function is to get called three times after initial loading of the window is complete:


$(function() {
    window.onload = function()
    {
        loadImages("ap", 9);
        loadImages("rs", 6);
        loadImages("ms", 16);
    };
    // Other document-ready actions
});
It's important to appreciate here that these three calls to loadImages() simply call startNext() once each, as the initial window load is completed, then exit. So the three calls are over very quickly. The rest of the interchange between startNext() and loadIt() then proceeds in parallel in the background.

As part of the normal page load, I set an animated 'working' gif as the background image for each of the placeholder DIVs that host the images once they are loaded.

More sophisticated strategies could be devised. For example, I could load the first two galleries in parallel, then use the last call to loadIt() to trigger loading of the final gallery by calling loadImages() again. I think though that this would be approaching paranoia ;=).