Wilsonhut

Deal with it or don't

Tag Archives: progress

$.defer for advanced promise users – progress, only when you need it!

EDIT: $.defer was renamed to $.whereas

[If you’re not familiar with $.defer, you can go read about it, or just go look at the code.]

Let’s say you have an long-running script inside a function, and you only want to do that work once, even though the function might get called more than once. That function might look like this:

function initializeTheDifficultStuff(){
 if ($(".importantStuff").data("isInitialized")){
   return;
 }
 // Do the difficult stuff

  $(".importantStuff").data("isInitialized", true);
}

The difficult stuff might be some ajax or some other long-running, dom-manipulating javascript.

Now, lets say you decide to make that use the $.Deferred() / promise jQuery feature. Modifying the code above, you could do something like this:

function initializeTheDifficultStuff(){
  if ($(".importantStuff").data("isInitialized")){
    return $.when(); // $.when() returns a resolved promise.
  }
  var d = $.Deferred();
  // Do the difficult stuff
  $(".importantStuff").data("isInitialized", true);
  d.resolve();  
  return d.promise();
}

Now that doesn’t get you much, except the ability to use the .then/.done methods when you call initializeTheDifficultStuff(), since the ‘difficult stuff’ is not asyncronous. If the ‘difficult stuff’ was an ajax call, and you put the d.resolve() in its callback, then you’d have something.

I discussed all of this in a previous post, but now I want to take it a step further…

What if you want to use the progress callback to show a ‘please wait…’ message?

What if you want to use the above method like this? (Here, I’m using the $.blockUI plug-in)

initializeTheDifficultStuff()
  .progress(function(){
    $.blockUI();
  })
  .done(function(){
    //.. show something, do something else.
    $.unblockUI();
  });

The reason you might WANT to do that is so that you could keep the $.blockUI call from happening if the “isInitialized” is already set. That’s what you want, since it’s not going to have to do the hard work in that case, and the $.blockUI call starts a fade-in which would be unnecessary.

Now that gets more difficult with the above implementation of initializeTheDifficultStuff, because, unlike the done and then, events, the progress is never called on a pre-resolved promise. So what can you do? You can return the d.promise, like above, but just before that, put a call to d.notify and d.resolve in a setTimeout. That would start to look ugly real fast. I’m not even going to show that.

$.defer handles this all for you.

You can use the initializeTheDifficultStuff() with the progress and done events, and have clean looking code by using $.defer, like so:

function initializeTheDifficultStuff(){
  if ($(".importantStuff").data("isInitialized")){
    return $.when(); // $.when() returns a resolved promise.
  }

  $(".importantStuff").data("isInitialized", true);
  return $.defer()
       .then(function(){
         // Do the difficult stuff
       });
}

The $.when() returns a resolved promise, so your progress event handler doesn’t get called!
$.defer returns an un-resolved promise. And in this example, it would wait the minimum number of milliseconds, then do the magic. Here’s what it will do for you…
It will first cause your progress event handler to get called. Then it runs the ‘difficult stuff’ for you. Then it is resolved so that your done event handler gets called.

It’s all so pretty and neat, and you get the progress event only when you need it!

Go get the $.defer plug-in from github today!

Advertisements