Platinum Partner
mobile,frameworks,javascript,tips and tricks,tools & methods

jQuery Deferred Object: Your New Best Friend

Here's your scenario: you are creating the splash page for an HTML5 mobile game. While you are loading resources, you display a wait spinner and a text message. This all works well except for when people have really fast internet connections - they don't have enough time to see the splash page or read the message. What you'd like to do is set a minimum amount of time for the message to show, say three seconds. But you aren't looking forward to writing a bunch of nested callback code. What should you do? Deferred object.

Deferred objects were introduced in jQuery 1.5. And with them came the end of lots of gnarly callback code - if only people would use them. Here is jQuery's official definition of a Deferred Object:

Description: A constructor function that returns a chainable utility object with methods to register multiple callbacks into callback queues, invoke callback queues, and relay the success or failure state of any synchronous or asynchronous function.

This is a quick post, so I won't fully explain Deferred Objects now. Instead I will show how to use them to solve the posed scenario. We will use three Deferred Objects. Two of them are passed to us by the Ajax get method, when we start to load our graphic and music resources. The third we will create ourselves for our three second timer. 

Once we have create all of our deferreds, we will display our loading message using jQuery Mobile's loading widget. Then we will use the setTimeout function to give us a three second timer. Once it fires we call the timerReady deferred object's resolve() method. This method does exactly what it name says, it sets the state of the deferred object as resolve.


The next bit of code is the when method. When is used to coordinate more than deferred object. Essentially it says once all of the indicated deferred objects have been resolved, run the code inside of me.


It is important to note that none of this code is synchronous. It is all asynchronous. The when method is like a callback, it gets fired once all of the objects gets resolved, whenever that may be.


For every deferred object in the when statement, you will be passed an array in the done method. This is most relevant with the ajax objects, since the array will contain the results of the ajax calls, but you could also create your own data array for your own deferred objects.

There are also a fail method and an always method. Since the always method is always called, it makes a great to remove the loading widget from the screen. 

Below is the code the for the splash screen complete with deferred objects. The complete code is at: https://github.com/Rockncoder/JQMGame. If you would like a more complete example please follow my plane game repo at: https://github.com/Rockncoder/planegame. There I will build the complete game over the next few months.

  RocknCoder.Pages.splash = (function () {
    return {
      pageshow: function () {
        // create our deferred objects
        // the Ajax get methods return deferred objects
        var timerReady = $.Deferred(),
          spriteMapReady = $.get("1945.png"),
          musicReady = $.get("DST-Afternoon.mp3");

        // put our load screen up
        $.mobile.loading( 'show', {
          text: "Loading resources...",
          textVisible: true,
          theme: "a"
        });

        // our timer simply waits until it times out, then sets timerReady to resolve
        setTimeout(function () {
          timerReady.resolve();
        }, 3000);

        // once all of my deferred objects have resolved, change the page
        $.when(timerReady, spriteMapReady, musicReady)
          .done(function (timerResponse, spriteMapResponse, musicResponse) {
            // let's put the data in our global
            RocknCoder.Resources = RocknCoder.Resources || {};
            RocknCoder.Resources.spriteMap = spriteMapResponse[0];
            RocknCoder.Resources.music = musicResponse[0];
          })
          // here you would check to find out what failed
          .fail(function () {
            console.log("An ERROR Occurred")
          })
          // the always method runs whether or not there were errors
          .always(function() {
            $.mobile.loading( "hide" );
            $.mobile.changePage("#attract");
          });
      },
      pagehide: function () {
      }
    };
  }());


Published at DZone with permission of {{ articles[0].authors[0].realName }}, DZone MVB. (source)

Opinions expressed by DZone contributors are their own.

{{ tag }}, {{tag}},

{{ parent.title || parent.header.title}}

{{ parent.tldr }}

{{ parent.urlSource.name }}
{{ parent.authors[0].realName || parent.author}}

{{ parent.authors[0].tagline || parent.tagline }}

{{ parent.views }} ViewsClicks
Tweet

{{parent.nComments}}