Pre-Process your images with promises

For this post, I’m using Windows 8 WinJS as the vehicle. However, this approach is applicable by any method that implements an XMLHttpRequest.

Here’s the scenario: you have an application with several images and those images are stored on a remote server. The sources of each image is something like http://someserver.com/myimage.png.

Looking at a Windows 8 store app as an example, the good news is that your page will display immediately. Your images may display immediately. Or more likely, they will begin to display after the page has loaded. You may see the images incrementally appearing. This does not make for a good user experience.

The good news is that WinJS implements the Promise Pattern and XMLHttpRequest via the WinJS.xhr object. This means we can pre-fetch images and resolve them to blob URL’s and then bind our UI to the blob URL instead of the raw image url.

Using a stock WinJS Navigation Project, the following code is placed in the app.addEventListener(“activated”, function (args) {} handler:

var promises = [];

//Load the blob for the default image in the event a specified image URL does not work.
WinJS.xhr({ url: "images/no-available-image.png", responseType: "blob" }).done(function 
(data) {
 var noImageBlob = URL.createObjectURL(data.response);
  //The imageResults.json file is a static result from the Bing Images API.
  WinJS.xhr({ url: "js/imageResults.json" }).done(function (data) {
     var results = JSON.parse(data.responseText);
     //Loop through the results to create the promise array.
     results.d.results[0].Image.forEach(function (element, index, array) {
     promises[index] = WinJS.xhr({ url: element.MediaUrl, responseType: "blob" })
        .then(function (e) {
           //When the promise executes, an imageBlob property is created
           //holding the image blob URL
           results.d.results[0].Image[index].imageBlob = URL.createObjectURL(e.response);
        }, function error(e) {
           //If an error occurs when trying to retrieve the image
           //the No Image BLob URL created on line 28 is used instead
           results.d.results[0].Image[index].imageBlob = noImageBlob;
        });
     });
     //The join method kicks off the promises handed to it.
     //The promises can run in any order and finish at any time. 
     //The done event for the join method fires after all of the promises
     //in the promise array have been fulfilled.
     WinJS.Promise.join(promises).done(function (e) {
        //Need to create a global namespace to hold a reference
        //to the images array. To facilitate data binding, 
        //the WinJS.BindingList method is invoked, passing the 
        //image array.
        WinJS.Namespace.define("ImageList",
           {
              Images: new WinJS.Binding.List(results.d.results[0].Image)
           });;
           //Now that all of the pre-processing has occurred, we can now
           //navigate to the home page that displays the images.
           if (app.sessionState.history) {
              nav.history = app.sessionState.history;
           }
           args.setPromise(WinJS.UI.processAll().then(function () {
              if (nav.location) {
                 nav.history.current.initialPlaceholder = true;
               return nav.navigate(nav.location, nav.state);
           }
           else {
                return nav.navigate(Application.navigator.home);
           }
        }));
     });
  });
);

JavasScript, being a dynamic language, we can simply augment the object from the parsed JSON. In this example, I used a static file from the Bing Image Search API. You will have to conform the object references to the specific API you are working with. In the previous code, we wait until all of the images have been fetch. In an async world, this can be a challenge. We don’t know what order the requests will be processed. In some cases, a server status 500 could result. This approach affords us an opportunity to handle that error with a place holder image. If on the other hand, you deal with the raw URL’s directly, you have no such opportunity to intercept errors.

On the HTML markup side, I have this code to bind an image tag’s src to the newly created imageBlob property. For this example, the raw URL that is returned in the response is MediaUrl.

<section aria-label="Main content" role="main">
   <div id="ImagesTemplate" data-win-control="WinJS.Binding.Template">
      <div style="width: 150px; height: 100px;">
          <img src="#" style="width: 60px; height: 60px;" 
              data-win-bind="alt: MediaUrl; src: imageBlob" />
      <div>
       <h4 data-win-bind="innerText: MediaUrl"></h4>
      </div>
   </div>
</section>
<div id="ImageListView" 
   data-win-control="WinJS.UI.ListView"  
   data-win-options="{itemDataSource : ImageList.Images.dataSource, 
                      itemTemplate: select('#ImagesTemplate')}">
</div>

In the markup, I have a WinJS ListView control that uses the ImageList.Images.dataSource as its datasource. The ListView also references a simple template to display the output.

Here is the result (looks a bit skewed due to this page’s dimensions):

images

When the page displays, because the images have already been pre-fetched, they will immediately display as well. This makes for a more pleasant user experience.

Looking at the first image, it has a raw URL of:

http://www.israbox.com/uploads/posts/2011-04/1303738133_john-coltrane.jpg

When pre-fetched, the blob URL is:

blob:354BAAC6-B8D8-4172-A283-4DF2F037FECD

About johnvpetersen

I've been developing software for 20 years, starting with dBase, Clipper and FoxBase + thereafter, migrating to FoxPro and Visual FoxPro and Visual Basic. Other areas of concentration include Oracle and SQL Server - versions 6-2008. From 1995 to 2001, I was a Microsoft Visual FoxPro MVP. Today, my emphasis is on ASP MVC .NET applications. I am a current Microsoft ASP .NET MVP. Publishing In 1999, I wrote the definitive whitepaper on ADO for VFP Developers. In 2002, I wrote the Absolute Beginner’s Guide to Databases for Que Publishing. I was a co-author of Visual FoxPro Enterprise Development from Prima Publishing with Rod Paddock, Ron Talmadge and Eric Ranft. I was also a co-author of Visual Basic Web Development from Prima Publishing with Rod Paddock and Richard Campbell. Education - B.S Business Administration – Mansfield University - M.B.A. – Information Systems – Saint Joseph’s University - J.D. – Rutgers University School of Law (Camden) In 2004, I graduated from the Rutgers University School of Law with a Juris Doctor Degree. I passed the Pennsylvania and New Jersey Bar exams and was in private practice for several years – concentrating transactional and general business law (contracts, copyrights, trademarks, independent contractor agreements, NDA’s, intellectual property and mergers and acquisitions.).
This entry was posted in Windows 8, WinJS and tagged . Bookmark the permalink. Follow any comments here with the RSS feed for this post.