Skip to main content

Mobomo webinars-now on demand! | learn more.

If you've upgraded to the recently released jQuery 1.5 you may have heard about the fancy new AJAX facilities that allow you to define things like this:

var jax = $.ajax({   url: '/some/url' })  jax.success(function() {   alert("It worked!");") });

Well, that new power is known as Deferreds and it can be useful in more places than straight-up AJAX callbacks. If you want an in-depth look at the Deferred API you can look at the jQuery Documentation or read this very in-depth look at the API. But we're here today to take a look at a few practical examples that will let you really get the most out of the new functionality.

A More Expressive setTimeout

I've always hated the Javascript API for setTimeout. In my opinion it just doesn't make sense to have the duration come after the function. Using Deferreds, we can write a very English-friendly syntax for timeouts.

$.wait = function(time) {   return $.Deferred(function(dfd) {     setTimeout(dfd.resolve, time);   }); }

Now, thanks to the Deferreds, I can write timeouts in my app like this:

$.wait(5000).then(function() {   alert("Hello from the future!"); });

This is just a small example, but it shows you how easy it is to write deferred functionality into your applications. For our next trick, let's get a little more complex.

Pretty Little Wrapper

A common thing I find myself doing in jQuery is writing a small wrapper for an API. I always hate how messy the callback code gets, let's see if we can clean it up a bit using Deferreds.

Twitter = {   search:function(query) {     var dfr = $.Deferred();     $.ajax({      url:"http://search.twitter.com/search.json",      data:{q:query},      dataType:'jsonp',      success:dfr.resolve     });     return dfr.promise();   } }

Now I can easily perform Twitter searches in my app like so:

Twitter.search('intridea').then(function(data) {   alert(data.results[0].text); });

The Advantages of Deferreds

Why would I want to use deferreds instead of standard callbacks? Well, for one, you can attach multiple callbacks to a deferred, giving you lots of flexibility in designing your application. Another way they're superior is by giving you built-in error handling. If you had a deferment that failed, you would be able to handle that as well:

function doSomething(arg) {   var dfr = $.Deferred();   setTimeout(function() {     dfr.reject("Sorry, something went wrong.");   });   return dfr; }  doSomething("uh oh").done(function() {   alert("Won't happen, we're erroring here!"); }).fail(function(message) {   alert(message) });

Deferreds are just a simple and powerful tool for you to use to make your code cleaner, more readable, and more functional. This is really just scratching the surface, so if you have any other fun Deferred tricks, I'd love to see them in the comments!

Update: From the comments, I see that it is appropriate to return dfd.promise() instead of the deferred object itself. The code has been updated to reflect as much.

Categories
Author

A problem that often arises in AJAX-based web applications is how to handle it when things go wrong. The user should be informed of the problem in a descriptive manner, but obviously the internal workings of the application should not be revealed. A generic and useful way to handle this problem is to use HTTP Status Codes to indicate the type of failure, and use the javascript to cope appropriately.

In this example I will be using jQuery, but the principles behind are just as applicable to Prototype or any other Javascript library.

Your Friends 403 and 500

Anyone who’s built a Rails site is familiar with the 500 error. That’s the generic exception code thrown whenever something goes wrong in your application. Users should, hopefully, never ever see this when they’re using the site. However, oftentimes there are exceptions or errors that users should experience just as result of improperly filling out fields, permissioning, etc. So how do we differentiate?

The Rails render method has the ability to render out an arbitrary HTTP Status Code (i.e. 500 for error, 404 for not found, and many more). The “403 Forbidden” code means that the server understood the request made of it, but is refusing to complete it. This sounds like an apt description for typically “caught” exceptions. So let’s use the 403 code to intelligently handle the exceptions that we want to be seen by the user.

In the controller

Let’s take the simplest example, an invalid record. If you are creating a record via AJAX, a flash[:error] with a render :action => "new" is not going to suffice. Let’s try something like this instead:

class ItemsController < ApplicationController   def create     Item.create!(params[:item])     # continue on your merry way if it works   rescue ActiveRecord::RecordInvalid => e     respond_to do |format|       format.html {          flash.now[:error] = "There was a problem creating the item."         render :action => "new"       }       # Render out the validation failed message with a       # 403 status code.       format.js { render :text => e.message, :status => 403 }     end   end end

All right, now that we’ve got it handled on the controller side, it’s time to work some Javascript magic on our AJAX call.

 $('a#ajax_link').click(function() {   $.ajax({     url: '/items',      success:function(data, textStatus) {       // do success things     },     error:function(request, textStatus, errorThrown) {       // Use the specific message for a 403, but       // a generic failure message for a 500       var message = (request.status == 403) ?          request.responseText : "An unknown error occurred. Support has been contacted.";       // Simple alert for example, but you can handle       // however you want, such as populating an error message       // div and making it appear.       alert(message);     }     return false;   }); }); 

Now when your AJAX request fails, it will render a user-friendly error message if it’s an ‘expected’ error or a generic message if the request fails with an unexpected exception.

This is a simple example, but by building a general framework for error expectations you can make it much easier to provide user-friendly error handling that gives them all of the information they need without revealing any of your internal processes.

Categories
Author
1
Subscribe to Ajax