Pitfalls of deferred/promise pattern

Posted April 17th, 2012 with No Comments

The Deferred/Promise pattern is great for two reason:

  • It decouples the when from the then statements: basically the operations that need to be fulfilled are completely separated from the code that will be executed once completed
  • The when instructions could be synchronous or asynchronous, it’s possible switch to an asynchronous operation in the $.when() fuction without changing anything else

So far so good, but we need to keep in mind that this pattern it’s just an abstraction and we should be always aware of where, in the code, our procedure is executed.

Consider this example where a popover is used to pick up some options: the whole process is handled with a deferred() object:

function openPopover() {
  var deferred = $.Deferred();
  $('#my_popover')
    // some instructions to handle the
    // popover
    .click(function() {
      // hide the popover
      $(this).hide();
      // resolve the promise
      deferred.resolve('my_choice');
      return false;
      })
  return deferred.promise();
  }

And the code to trigger the popover is

$.when(openPopover())
  .then(function() { 
    very_long_operation();
    });

What is not clear here is that the code of the function very_long_operation() is actually executed within the handler of the .click(), after the .hide() operation but before the click handler is completed.
Here is the point: if the function very_long_operation() turns out to be a long process, say 1 or 2 secs, it will take this interval for the popover to disappear after the click, since the DOM will be updated only at the end of the event handler.
From the user perspective, this results in a less responsive user interface.

This is particulary bad since we want to decouple the when from then: openPopover() doesn’t know anything about very_long_operation(), nevertheless wether is a short or long process.

The smartest solution could be a WebWorker: refactor each lines of very_long_operation() in order to exploit HTML5, but this will not be available on oldest browsers.

The quickest solution is and old-quick-and-dirt setTimeout:

$.when(openPopover())
  .then(function() { 
    setTimeout(very_long_operation,1);
    });

It just detach the long process very_long_operation() from the event loop, letting the click handler to complete (and update the DOM), and everything is safe: the user interface and our decoupled code.

The same code using Stackable:

$.when(openPopover())
  .then(function() { 
    $function(very_long_operation).fork();
    });

PS: The same result could be achieved by wrapping with the timeout trick the line deferred.resolve(‘my_choice’); but this is wrong from the point of view of code decoupling: the popover code knows nothing (and doesn’t have to) about the code executed with the resolve() statement, putting the timeout here would imply that our deferred object always assumes to deal with very long process.
Deferreds is all about decoupling

  • Categories

  • Tags

  • Enter your email address to subscribe to this blog and receive notifications of new posts by email.

    Join 2 other subscribers

  • Follow me on Twitter