Vasanth Krishnamoorthy

Software Engineer (Http'ster)

What’s the deal with jQuery Deferred objects and Promises?

You’ve probably heard people around the water cooler about how handy promises are with handling asynchronous operations in JavaScript. All of the cool kids are using them, but are you wondering what’s the big deal about it? Well, let’s dive into it and see how we can use them.

Asynchronous Operations

Before showing how promises work, let’s first look into a standard asynchronous function. The following example function takes a callback function as parameter which is executed after a certain time delay.

function testAsync(callback) {
    setTimeout(function() {
 		callback();
 	}, 1000);
}

Calling the async function would look like this,

testAsync(function() {
    console.log("Call me maybe");
});

The most common cases when asynchronous work is being executed are:

  • AJAX calls
  • Loading resources over the network
  • setTimeout() or setInterval()
  • Animations
  • User interaction

When an AJAX call is executed it will be executed asynchronously by the browser. Once the AJAX call completes some callback function is called by the browser.

Deferred objects in jQuery

Deferred objects in jQuery represents a unit of work that will be completed later, typically asynchronously. Once the unit of work is complete, the deferred object can be set to resolved or failed. A deferred object contains a promise object. With the promise object you can specify what is to happen when the unit of work completes by setting callback functions.

Promises

Credits: http://blog.mediumequalsmessage.com

 

The testAsync() function which we looked into earlier can also be implemented using a jQuery deferred object like this,

function testAsyncDeferred() {
    var deferredObject = $.Deferred();
	setTimeout(function() {
 		deferredObject.resolve(); 
 	}, 1000);
 	return deferredObject.promise();
}

What really happens?

  • A deferred object is created using the jQuery $.Deferred() function. It does nothing by itself, but is just a representation of some asynchronous work.
  • A function is passed to the setTimeout() function. This function gets executed after a delay and then deferredObject.resolve() is called. The internal state of the deferred object is now changed to resolved (after the delay).
  • Finally, the promise object associated with the deferred object is returned. Calling the testAsyncDeferred() with the promise object let’s us specify what should happen if the deferred object’s state changes to resolved or failed.

Calling the testAsyncDeferred() function would look like this,

var promise = testAsyncDeferred();

promise.done(function () {
    console.log("Call me maybe");
});

Notice how the callback function is no longer passed as parameter to the testAsyncDeferred() function. Instead, the callback function is passed to the done() function on the promise object. Once the deferred object’s state changes to resolved, the callback function passed to done() will get executed.

Difference between a Deferred and a Promise

A promise is a dynamically generated object which lets clients hook onto our done and fail callbacks but doesn’t have access to the .resolve .reject, and other methods that change our original Deferredobject. A promise is an immutable object, meaning clients can listen to it but they can’t really do anything to change it. We want this functionality because we don’t want someone else to come along and accidentally resolve our Deferred. So instead of returning the Deferred we just created, we only return its promise.

What’s the deal with promises?

The thing is, promises are not just about callback aggregation but also act a direct correspondence between synchronous functions and asynchronous functions.

In synchronous functions you can feed the return value of one function directly to another and there are only 2 things that could possible happen:

  • function return values
  • function throws exceptions

They both are essentially means by which we implement composition. If at any point the process fails it can throw an exception which we can later handle in a catch block.

However, in an asynchronous world, you can no longer return values or throw exceptions, because there’s nobody to catch them. So we usually go for the so-called “callback hell,” where composition of return values involves nested callbacks, and composition of errors involves passing them up the chain manually.

The point of promises is to give us back functional composition and error bubbling in the async world. They do this by saying that your functions should return a promise, which can do one of two things:

  • Become fulfilled by a value
  • Become rejected with an exception

In other words the then function will compose just like their synchronous counterparts, with fulfillments flowing up a compositional chain.

In other words, the following asynchronous code:

getScoresFor("cricketwc2015") // promise-returning function
     .then(function (scores) {
	    var allScores = parseAllScores(scores);
    	var recentGame = allScores[0];
    	return getCompleteScoreCard(recentGame); // promise-returning function
  	})
  	.then(httpGet) // promise-returning function
  	.then(
    	function (responseBody) {
    	  	console.log("Most recent Scorecard:", responseBody);
    	},
    	function (error) {
      		console.error("Error with the Cricket Scores API:", error);
  		  }
  	);

 is the same as the synchronous code:

try {
    var scores = getScoresFor("cricketwc2015"); // blocking
  	var allScores = parseAllScores(scores);
    var recentGame = allScores[0];
  	var responseBody = httpGet(getCompleteScoreCard(recentGame)); // blocking x 2
  	console.log("Most recent Scorecard:", responseBody);
} catch (error) {
  	console.error("Error with the Cricket Scores API: ", error);
}

Note that the error flows from any step in the process to our catch handler, without any explicit bubbling code. With ECMAScript 6 and task.js shenanigans, the code becomes not only parallel but almost identical.

Great times ahead, I promise(); 🙂

 

 

 

16 Comments

  1. Matthew

    Hey, not sure if you knew this but none of the normal scrolling controls work anywhere on your website. The only way that works is using the thing you get when you click the middle mouse button. I’m using Chrome 40 on a windows desktop.
    That aside, great post!

    Reply
    1. Vasanth K

      Unfortunately I wasn’t able to reproduce it and haven’t heard about it from anyone else. Could be an one-off issue. Let me know if the issue persists. Thank you 🙂

      Reply
      1. Joel

        FYI, I looked through Chromes tools to find the event handler for the mousewheel and found smoothscroll.js attached to it. When I disabled it then the scrolling by mousewheel worked. So it must have something to do with that library. No issue in firefox or IE…

        Reply
    2. Joel

      Me too! On the latest Chrome and pageUp/Down, up/down arrows, and mouse scroll do not work at all. Have to physically use the mouse to control the scrollbar like in the Windows 3.1 day. Ouch! But I liked the artical….

      Reply
  2. David

    Thanks for the article.
    Re: scrolling…I can scroll in Chrome, but it is like sliding on ice. A bit of scrolling ends up flying through the page. It still works, but taking over scrolling with JS makes for a rough reading experience.

    Reply
    1. Bob Stein

      My bad, .then() is just a generic for .done() .fail() etc. Duh. Still, the jump to the asynchronous / synchronous code comparison is a surreal experience. Whether that’s because chained-thens are too advanced for me, or the code comparison is not correct, I can only wonder.

      Overall this article was a huge help comprehending the distinction between Deferred and Promise objects.

      Reply

DROP A COMMENT

Your email address will not be published. Required fields are marked *