Everything you need to know about jQuery Ajax Promise and Deferred

June 29th, 2018

Many times in our daily lives, we need to make decisions about the future depending upon an anticipated outcome. Let’s take a real-world analogy.

We decided to watch a movie and go to buy tickets and outcome is either get the tickets or don’t but we need to have a plan irrespective of the outcome. For example, we may need to book a cab to another place for the visit if unsuccessful in buying ticket, or on successful watch the movie.

In terms of coding it looks like below :

person.buyingTicket().then(
    successFunction(){
         //watch movie.
    },
    failureFunction(){
        //hire cab for another place
    }
);

Now the main point is that even though we don’t know the outcome of the buyingTicket function, we are able to chain a method to the ‘supposed’ outcome and specify the functions that should be invoked upon completion of the ‘buyingTicket’ function.

This is possible because the buyingTicket function would return a ‘Promise’ object.

 Promises are important because of three main reasons:

 1) It helps to decouple the logic of future handlers from the actual task invocation.

 2) We can add any number of future handlers.

 3) If we add a handler to an already resolved Promise, the appropriate(success of failure) function would fire immediately.

The advantages of using a promise is more when used in the context of Ajax requests

Let’s understand the jQuery deferred object in terms of an ajax request.

We all used jQuery ajax method to get, post data on the server and have to wait for the response until request complete. There is only one success and one error callback in typical ajax method. Programmers are  totally dependent on these two methods to move further and its implementation look like below



$.ajax({
	url: "/someurl",
	method: "GET",
	data: {
		 a: "a"
	},
	success: function(data) {
		 console.log('success', data)
	 },
	error: function(xhr) {
		 console.log('error', xhr);
	}
})

OR

function getData (){
	 $.get('/page.php',function(data){
		console.log(data) //filled!
		 return data;
	});
}
var results = getData();
console.log(results); //empty!

Issues:-


a) When using options for callbacks, we can’t pass more than 1 callback for success or fail option
b) getData() method run before ajax call so result log is empty and we DON’T want to do is to continue our logic inside the $.get() method
c) Empty result return from getData function.

and if we have chain of multiple ajax call then it look ugly


$.ajax({
  success: function() {
	$.ajax({
		success: function() {
			$.ajax({});
		});
	 }
});

As well as not easy to handle errors and understood.

Solution:-

To fix this issue jQuery introduced the concept of deferred and promise.jQuery have promises implemented with their AJAX methods. They allow us to work with events that have completed or put them in queues or chain them.

So above examples look like :-


$.ajax({
	url: "/someurl",
	method: "GET",
	data: {
	 a: "a"}
}).done(function(data) {
	 console.log('success', data)
}).fail(function(xhr) {
	console.log('error', xhr);
});

OR


function getData (){
	return $.get('/page.php');
}
getData().done(function(data){
	console.log(results); //filled!
});

getData().fail(function(data){
	console.log(data); //fail!
});

The promise object contains the following functions we can call:



done() ==> It run on success		
fail() ==> It run when our  call fails		
always() ==> It run every time
var promise = $.ajax({
	url: "/someUrl"
});
promise.done(successFunction);
promise.fail(errorFunction);
promise.always(alwaysFunction);

OR we can chain methods like

var address = $.ajax({});
var tweets = $.ajax({});
var facebook = $.ajax({});
render_side_bar = function(address, tweets, facebook){
//render sidebar
}
render_no_side_bar = function(){}
$.when(address, tweets, facebook).then(
	render_side_bar,
	render_no_side_bar
)

Easy to understand and handling errors. We can bind any number of callbacks to .done and .fail like

$.ajax({
	url: "/someurl",
	method: "GET",
	 data: {a: "a"}
}).done(function(data) {
	 console.log('success callback 1', data)
}).done(function(data) {
	 console.log('success callback 2', data)
}) .fail(function(xhr) {
	 console.log('error callback 1', xhr);
}).fail(function(xhr) {
	console.log('error callback 2', xhr);
});

we can chain them like:-



$.ajax( "url" )
.done(function() { alert("success"); })
.fail(function() { alert("error"); })
.always(function() { alert("complete"); });

Deferred


Deferred objects in jQuery represent a unit of work that will be completed later, typically asynchronously. Once the unit of work completes, the deferred object can be set to resolved or failed.

A deferred object also has a done() and fail() function, just like the promise object has the main difference between returning the deferred object and returning the promise object is that the deferred object can be used to set the resolved or failed state of the deferred object. You cannot do that on the promise object.

Every jQuery Promise begins with a Deferred. A Deferred is just a Promise with methods that allow its owner to resolve or reject it.

It has two important methods:

1) Resolve
2) Reject

And it has three important “events” or ways to attach a callback:

1) done
2) fail
3) always,

By creating a new Deferred object via the jQuery.Deferred() method, we can set up our own deferred processes.

var promise = doAsync();
promise.done(function() { console.log("done 1"); } );
promise.fail(function() { console.log("fail 1"); } );

function doAsync(){
	var deferredObject = $.Deferred();
	setTimeout(function() {
		var randomValue = Math.random();
		if(randomValue < 0.5) {
			deferredObject.resolve();
		} else {
			deferredObject.reject();
		}
	}, 1000);
	return deferredObject.promise();
}

if we call the resolve method then done callback will be executed whereas on calling “reject” method the fail callback attached will be executed, the always callback is executed whether the deferred is resolved or rejected.

Combining promises with $.when

Another very useful method is $.when. The method accepts an arbitrary number of promises, and it returns a

master deferred that:

will be “resolved” when all the promises are resolved,
will be rejected if any of the promises is rejected,
The done callback has the results of all the promises.

function getData1 (){
	 return $.get('/page1.php');
}
function getData2 (){
	return $.get('/page2.php');
}
function getData3 (){
	 return $.get('/page3.php');
}
$.when(getData1(),getData2(),getData3()).done(function(r1,r2,r3){
	 console.log(r1) //[data, status, xhrObj]
	console.log(r2) //[data, status, xhrObj]
	console.log(r3) //[data, status, xhrObj]
});

function defCalls(){
	 var def = $.Deferred();
	$.when(getData1(),getData2(),getData3()).done(function(r1,r2,r3){
		 def.resolve(r1,r2,r3);
	})
	return def.promise();
}
defCalls().done(function(d1,d2,d3){
	//now we have access to data...
})

we want to create an action for failed events, we use the .reject() method for that:



function defCalls(){
	var def = $.Deferred();
	$.when(getData1(),getData2(),getData3()).fail(function(){
		def.reject();
	})
	return def.promise();
}
defCalls().fail(function(){
	//do something here
})

There are many benefits in using promises and deferred objects – especially in asynchronous programming with jQuery’s AJAX. Not only it will make your code easier to read, but it also makes it easier to debug, well factored and organized – the way jQuery intended it to be.

Some common examples :-

Here is how one would request using a version of jQuery prior to 1.5


$.ajax({
	url: "test.html",
	success: function(){
		alert("ajax request succesful");
	},
	failure: function(){
		alert("ajax request failed");
	}
});

It can be seen that you are bound to specify the success function while making the ajax request. Moreover, there is only one available callback for success and failure each.

Now invoke the same ajax call that we saw earlier using a version of jQuery > 1.5.

var myRequest = $.ajax({ url: "test.html" })
.done(function(data){
	alert('ajax request was successful');
})
.fail(function(data){
	alert('ajax request failed');
});
//After a few more lines of code
myRequest.done(function(data){
	$('div.myclass').html('Ajax Response ' + data);
});

It is far better than the previous method of invocation of the ajax request. The done and fail functions are used to register callbacks on the object returned by the ajax call. The ajax call returns a Deferred object which implements the Promise interface. Since its a promise, we can attach handlers to it so that when the request completes, the future handlers would be invoked.

A common way to animate the scroll position of a webpage with jQuery is to use the following example:


$('html, body').animate({ scrollTop: 2000 }, { complete: function () {
	console.log('complete');
}});

This works well, but it will fire the “complete” callback twice – once for the HTML element and once for the body element. Many times where we needed a callback to fire only once when the scrolling animation was finished. Every jQuery object has a “promise” method, which returns a promise that, by default, resolves when all current animations on that object are complete. So the above example can be rewritten even more simply as:



$('html, body').animate({ scrollTop: 2000 }).promise().then(function () {
	console.log('complete');
});

The trick that makes sure the last image loaded will always be the most recent image displayed in the “stage” is to create a promise that will be resolved when the image is loaded.




function loadImage(url) {
	cancelLoadImage();
	return $.Deferred(function (dfd) {
		cancelLoadImage = dfd.reject;
		var img = new Image();
		$(img).on({
			load: function () { dfd.resolve(img) },
			error: dfd.reject
		});

		img.src = url;
	}).promise();
}

function cancelLoadImage() {}
function updateStage(img) {
	$('#stage').html(img);
}

loadImage('/images/animation-1.gif').then(updateStage);

loadImage('/images/animation-2.gif').then(updateStage);

The key step is the “cancelLoadImage” function, which starts off as an empty function that does nothing. Every time “loadImage” is called, it will call “cancelLoadImage.” When a new promise object is created, it will set “cancelLoadImage” to the reject method of the promise, ensuring that each new call to “loadImage” will always reject the previous promise, preventing its resolved callbacks from executing.