Friday, November 8, 2013

jQuery Deferred Object - Creating Your Own

In my last post, jQuery Deferred Object - Your New Best Friend, I gave a quick overview of what a deferred object is and how to use it. This time let's go a bit deeper and explore creating your own deferred object for a much more practical use - asynchronous downloading and processing of audio files.

One of my favorite web sites is HTML5Rocks. They have all of the latest information on developing cutting edge HTML5 web apps. As I continue building my HTML5 game, I've turned to them for more information on several topics including the Web Audio API. In one of their articles,  Getting Started with Web Audio API, they create a BufferLoader class.


I thought it would be fun to convert this class to use a deferred object and streamline it even more by getting rid of the object constructor and instead use a closure. So let's get started.

Getting Rid of the Constructor
The BufferLoader uses a function as an object creator. There is nothing wrong with that per se, it is just a pattern which I rarely use these days. Instead I normally prefer to use a closure. Think about why they are creating an object. Since both the loading and the converting of the files are asynchronous operations they want to be to support multiple calls into the same code. Using a constructor will guarantee that each instance has access to its own set of variables and won't interfere with any other call. A closure does the same thing, but more succinctly. So we will delete the constructor function. We also will wrap our entire code in a function to isolate from any other code in the browser. We use a single global object, RocknCoder, to hold all of our global stuff. We make the loadBuffer() method an internal method of the new RocknCoder.loadAudioFiles() method. This also makes it accessible globally.

Creating the Closure
Most of the instance variables are moved or passed into the loadAudioFiles() method. The this variable or its alternate name, loader, is no longer necessary, so we delete it to. All of its uses become the name without the "loader.", for example "loader.context" becomes simply "context". I don't know about you, but too much using of "this" in JavaScript, gets really confusing. To create the closure we call the loadBuffer method. When this method is called, it essentially creates a snapshot of the environment, all of the variables and their values become frozen, so that when an async callback happens, the state of the variables is restored. Note the subtle change of the parameters passed to loadBuffer(). Previously it was passed a single URL and index which was to the bufferList[] array. Now it is passed all of the URLs as an array and an index value which indicates which URL is currently being loaded. Once a URL is loaded and processed, index is incremented and if we haven't loaded all of the URLs yet, loadBuffer() is called recursively. Do it this way also eliminates the need for load() function, so we delete it too.

Making Use of the Deferred Object
Right off the back in the loadAudioFile() method, we create our deferred object by calling the jQuery $.Deferred() method. The last line of the method returns the deferred object, myDeferred to the caller. Keep mind that this method, will continue to run even after it returns to the caller since their are multiple asynchronous callback going on here. In the method we curiously use the XMLHttpRequest object instead of using jQuery's version of it. This is mainly because the XMLHttpRequest object supports the "arraybuffer" type which allows us to load binary data, very important for audio.

If we encounter an error along the way we can call the reject() method and pass some error information all with it. This will cause our deferred object to fail and the information passed to it will be available to the fail() method.

Once all of our audio files has loaded and been processed, we simply resolve() our deferred object. In this case we pass our bufferList, which contains all of our processed audio files to our deferred object. This will make them available to our done() method. Also note how we eliminated the need to track the index count on bufferList by using the push() method instead an index.

We could have also used the deferred object's progress() method each time we successfully finished either loading or processing a file. That would allow the caller to update a UI element to keep the user informed as to our progress. Oh well, maybe next time.

Summary
We started with roughly 46 lines of code in three methods and converted into roughly 42 lines in one method. Doesn't seem like too great of a win but the code is cleaner to read and most of the win is for the caller. Instead of having to deal with a single callback, where they have to sort out the results. They now get separate done() and fail() methods. Plus the deferred object passed here can be combined with others in a when() statement. An example of this is in the game I am building. We wait for all of the audio files, the sprite map, and a minimum of three seconds to pass before we leave the splash screen. An example of which is below.

If you would like to see more of the game, be sure to check out my repo on GitHub at: https://github.com/Rockncoder/planegame. Be sure to check out the first post of this series: jQuery Deferred Object - Your New Best Friend.

Thursday, November 7, 2013

jQuery Deferred Object - Your New Best Friend

Part 2 of this series is at: jQuery Deferred Object - Creating Your Own

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.

Part 2 of this series is at: jQuery Deferred Object - Creating Your Own



Sunday, November 3, 2013

Mobile Device Detection and Redirection in Node+Express

One of the challenges of writing a mobile web site is what to do if a user hits the site from a desktop? Of course, some will say that you should simply make the site responsive, able to look good regardless of the browse it is displayed on. But what if the site is a game? I know all of the responsive design true believers will be upset with my solution, but so what, I like it. I "iframe" the mobile site into a 320 x 480 window on the desktop page. This brings up more challenges, like how detect a mobile device and how to route to the correct page?

The first challenge has already been solved by the excellent web site: http://detectmobilebrowsers.com/. They have created a rather long regular expressions which detects a mobile browser by its user agent. The answer to the second challenge comes from the Express.js middleware which makes Node.js a great framework. 

Express allows the developer to inject middleware into the route handler. For our root route we will call the checkForMobile method. If we detect a mobile browser, we will redirect to our mobile route. On all other routes, we won't bother checking for mobile. If a desktop user wishes to navigate directly to the mobile page, we will let them.

Please check out the code sample below to see how it is done. Or if your are interested in the complete, and still in progress, mobile game check it out at: https://github.com/Rockncoder/planegame. To see the live and very much in progress code, go to: http://planegame.herokuapp.com/

I will be talking about the complete game at So Cal Code Camp at USC, the weekend of November 9th & 10th. If you are in the area, be sure to attend. It is a free event: http://www.socalcodecamp.com/