How Can Jquery Deferred Be Used

How can jQuery deferred be used?

The best use case I can think of is in caching AJAX responses. Here's a modified example from Rebecca Murphey's intro post on the topic:

var cache = {};

function getData( val ){

// return either the cached value or jqXHR object wrapped Promise
return $.when(
cache[ val ] ||
$.ajax('/foo/', {
data: { value: val },
dataType: 'json',
success: function( resp ){
cache[ val ] = resp;
}
})
);
}

getData('foo').then(function(resp){
// do something with the response, which may
// or may not have been retrieved using an
// XHR request.
});

Basically, if the value has already been requested once before it's returned immediately from the cache. Otherwise, an AJAX request fetches the data and adds it to the cache. The $.when/.then doesn't care about any of this; all you need to be concerned about is using the response, which is passed to the .then() handler in both cases. jQuery.when() handles a non-Promise/Deferred as a Completed one, immediately executing any .done() or .then() on the chain.

Deferreds are perfect for when the task may or may not operate asynchronously, and you want to abstract that condition out of the code.

Another real world example using the $.when helper:

$.when($.getJSON('/some/data/'), $.get('template.tpl')).then(function (data, tmpl) {

$(tmpl) // create a jQuery object out of the template
.tmpl(data) // compile it
.appendTo("#target"); // insert it into the DOM

});

Use case of jquery Deferred

The general trend with promise development is to not use a Deferred object or Deferred constructor any more. In fact, the ES6 standard for promises does not even support such a thing because it simply isn't needed. There is always an alternative way to code things that does not create a Deferred object.

In your particular case with your customAjax method, you don't need it at all. Instead, you can just do this:

function customAjax() {
return $.ajax({
type: 'GET',
url: 'abc/blala'
}).then(function (result) {
// process the result in any way here
return processed result here
});
}

customAjax().then(function(value) {
// do something with value here
});

So, in your situation above, there is no need to create your own promise. You can just use the promise that $.ajax() already returns and just chain onto it and return it.

Note: I also changed your code to use .then() because that is the "standard" way of doing things and is already supported by jQuery. jQuery is moving to a much more standards-compatible promise implementation in jQuery 3.x so it's wise to start coding that way now.


If you do need to create your own promise in jQuery 1.x or 2.x and want to do it as compatible as possible with the ES6 standards, while still using the jQuery supported stuff, you can do something like this:

function delay(t) {
return jQuery.Deferred(function(def) {
setTimeout(function() {
def.resolve();
}, t)
}).promise();
}

For reference purposes, the ES6 standards syntax would look like this:

function delay(t) {
return new Promise(function(resolve, reject) {
setTimeout(function() {
resolve();
}, t)
});
}

As for when to use what with $.ajax(), life is a bit confusing. $.ajax() supports a zillion different ways to get notified when it's done or when there's an error. There are straight callbacks passed as arguments. There's .success() (which is now deprecated). There's .done() which is promise-like, but non-standard. There's .then() which is almost standard.

In my opinion, there are huge advantages to using one and only method and making that method be the standard promise mechanism with .then(). This then allows you to have all the other advantages of using promises for async operations which include:

  1. The ability to chain multiple async operations together in a sequence.
  2. The ability to coordinate multiple async operations using things like $.when() or Promise.all().
  3. Throw safety even in async operations (this will require ES6 compatible promises in jQuery 3.x).
  4. Significantly enhanced error propagation, even from deeply nested async operations.
  5. A "standard" way to code async operations.

What are deferred objects?

Deferred Object

As of jQuery 1.5, the Deferred object provides a way to register multiple callbacks into self-managed callback queues, invoke callback queues as appropriate, and relay the success or failure state of any synchronous or asynchronous function.

Deferred Methods:

  • deferred.done()
    • Add handlers to be called when the Deferred object is resolved.
  • deferred.fail()
    • Add handlers to be called when the Deferred object is rejected.
  • deferred.isRejected()
    • Determine whether a Deferred object has been rejected.
  • deferred.isResolved()
    • Determine whether a Deferred object has been resolved.
  • deferred.reject()
    • Reject a Deferred object and call any failCallbacks with the given args.
  • deferred.rejectWith()
    • Reject a Deferred object and call any failCallbacks with the given context and args.
  • deferred.resolve()
    • Resolve a Deferred object and call any doneCallbacks with the given args.
  • deferred.resolveWith()
    • Resolve a Deferred object and call any doneCallbacks with the given context and args.
  • deferred.then()
    • Add handlers to be called when the Deferred object is resolved or rejected.

Deferred In Action:

$.get("test.php").done(
function(){ alert("$.get succeeded"); }
);

$.get("test.php")
.done(function(){ alert("$.get succeeded"); })
.fail(function(){ alert("$.get failed!"); });

And it seems that the existing ajax() method callbacks can be chained rather than declared in the settings:

var jqxhr = $.ajax({ url: "example.php" })
.success(function() { alert("success"); })
.error(function() { alert("error"); })
.complete(function() { alert("complete"); });

Working Example From Eric Hynds blog post: http://jsfiddle.net/ehynds/Mrqf8/


jqXHR

As of jQuery 1.5, the $.ajax() method returns the jXHR object, which is a superset of the XMLHTTPRequest object. For more information, see thejXHR section of the $.ajax entry



From JQUERY 1.5 RELEASED:

DEFERRED OBJECTS

Along with the rewrite of the Ajax
module a new feature was introduced
which was also made publicly
available: Deferred Objects. This
API allows you to work with return
values that may not be immediately
present (such as the return result
from an asynchronous Ajax request).
Additionally it gives you the ability
to attach multiple event handlers
(something that wasn’t previously
possible in the Ajax API).

Additionally you can make your own
deferred objects using the exposed
jQuery.Deferred. More information
about this API can be found in the
Deferred Object documentation.

Eric Hynds has written up a good
tutorial on Using Deferreds in jQuery
1.5.

How to apply a deferred object to a large block of code?

First off, I'd suggest changing this:

function function1() {
// create a deferred object first
var dfrd1 = $.Deferred();

// mimic an async request
setTimeout(function() {
// lots of async stuff here
// resolve the deferred object
dfrd1.resolve();
}, 1000);

// return a promise()
return dfrd1.promise();
}

function1().done(function() {
// when function1 is done, do something
console.log('function1 is done!');
});

to this:

function function1() {
// create a deferred object first
return $.Deferred(function(def) {
// mimic an async request
setTimeout(function() {
// lots of async stuff here
// resolve the deferred object
def.resolve();
}, 1000);
}).promise();

}

function1().then(function() {
// when function1 is done, do something
console.log('function1 is done!');
});

Your original code works fine and contains no anti-patterns, but this new code has these advantages:

  1. Using the callback to $.Deferred() is closer to how the ES6 promise standard works with new Promise() so it's generally a good idea to move your programming in the direction of the standards.

  2. Use .then() instead of .done(). Again, .then() is how the ES6 promise standards work and jQuery supports it just fine. If at some point in the future, you changed function1() to use an actual ES6 promise instead of jQuery promise, then your function1().then() would continue to work just fine.


In jQuery, an Ajax request already returns a promise. There is no need to wrap it in a deferred and, in fact, doing so is an anti-pattern (creating a promise when you don't need to create a new one).

Promises have NO magic powers to just know when asynchronous things are done. Instead, some code has to specifically call .resolve() when the asynchronous operation is done. And, in the case of jQuery Ajax functions, this is done automatically for you with the promise that any of the jQuery Ajax functions return. So, you can just do this:

function largeFunction(myVar) {

return $.ajax(...);

}

largeFunction(....).then(function(results) {
// handle successful results here
}, function(jqXHR, textStatus, errorThrown ) {
// handle error here
});

If you have multiple Ajax functions or asynchronous operations inside largeFunction(), then you can use promises to chain them (which will sequence them) or coordinate them and still return a single promise that represents when everything is done and represents the desired final results. jQuery provides coordination tools such as $.when() to know when multiple promises are done (this is Promise.all() in the ES6 standard).

JQuery Deferred how to use resolve and promise to determine function end

Your promise is resolving only when your setTimeout function is calling r.resolve() after 4500ms. You need to call r.resolve() when all your stuff is done. Maybe something like this...

// function definition
var jslongfunc = function(defr) {
//... do lots of stuff here ...
defr.resolve();
}

// promise helper
var promise = function (fn) {
var dfd = $.Deferred(fn);
return dfd.promise();
}
// init the deferred by using the promise helper above and then attach 'done' callback to stop spinner
var rdef = promise(jslongfunc).done(function (response) {
spinner.stop();
}).fail(function (response) {
// handle promise failure.
});

Update

Well now that you put up your using $.getJSON you can do the following.

function jslongfunc(value) {
var jqxhr = $.getJSON(url, function (data) {
// THIS IS YOUR FIRST promise callback
// do some stuff with the json you just got
});
// return the promise
return jqxhr;
}

// now handle the spinner stoppage
jslongfunc(value).done(function () {
spinner.stop();
}).fail(function (data) {
// handle function failure here
});

jQuery.Deferred().then, how to resolve with multiple parameters

You will have to invoke resolve() or reject() in order to pass multiple arguments.

.then() doesn't include any mechanisms for "spreading" a returned collection. It'll just keep the collection intact as the 1st argument.

But, it will interact with a Deferred or promise that's returned. From the paragraph starting with "As of jQuery 1.8":

These filter functions can return a new value to be passed along to the promise's .done() or .fail() callbacks, or they can return another observable object (Deferred, Promise, etc) which will pass its resolved / rejected status and values to the promise's callbacks.

So, you could use your example of other() as the basis for fn() to keep it fairly succinct with another Deferred():

function fn() {
return other().then(function () {
return $.Deferred().resolve(1, 2).promise();
});
}

fn().then(function (a, b) {
console.log(arguments.length, a, b); // 2 1 2
});

http://jsfiddle.net/cqac2/

jquery deferred and return false based on server response

You should provide success callbacks then for your $.getJSON and return custom Deffered for $.when to handle.

That way you can manually resolve or reject based on data that's in the JSON.

var $qCallA = callA();
var $qCallB = callB();

$.when($qCallA,$qCallB).then(function (s1, s2) {
$("#spinnerDiv").removeClass('spinner show');
}).fail(function() {
//handle failure
});

function callA() {
return $.getJSON("/callA").then(function (data) {
if (data.status === 'failure') {
return $.Deferred().reject("A: no success");
}
return $.Deferred().resolve(data);
});
}

function callB() {
return $.getJSON("/callB").then(function (data) {
if (data.status === 'success') {
return $.Deferred().resolve(data);
}
return $.Deferred().reject("B: no success");
});
}

Similar JSFiddle

jQuery deferred: Nesting promises in methods, last callback too early

You need to return the promises inside your callback functions. That's the only way the promise chain can wait on them.

Also, you're abusing the Explicit Promise Creation Antipattern.

.then() returns a promise. You can just return that. And there's no point in passing a single promise into $.when() because all $.when() is going to do is spit it right back out:

function first() {  $('ul').append("<li>first started</li>");  let deferred = $.Deferred();  setTimeout(function() { // Any async function.    $('ul').append("<li>first ended</li>");    deferred.resolve();  }, 2000);  return deferred.promise();}
function second(da) { $('ul').append("<li>second started</li>"); let deferred = $.Deferred(); $('ul').append("<li>second ended</li>"); deferred.resolve(); return deferred.promise();}
function third(da) { $('ul').append('<li>third started</li>') let deferred = $.Deferred(); setTimeout(function() { $('ul').append("<li>third ended</li>"); deferred.resolve(); }, 2000);
return deferred.promise();}
function GroupFunction1() { let deferred = $.Deferred(); $('ul').append("<li>Group 1 started</li>"); var data = "test2";
// v-- here return first() // v-- here .then(function() { return second(data); }) .then(function() { return third("test2"); }) .then(function() { $('ul').append("<li>Group 1 ended</li>"); });}
function GroupFunction2() { $('ul').append("<li>Group 2 started</li>");
$('ul').append("<li>Group 2 ended</li>");}
$(function() { GroupFunction1().then(GroupFunction2);})
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script><ul></ul>

Chaining jQuery deferred by updating deferred inside then()?

You don't need to use when, which is really better used when you want to run multiple deferreds in parallel. Try this:

var d1 = $.ajax(...);
var d2 = d1.then(function () {
if (invalid) {
return $.Deferred().reject("Invalid");
}
return $.ajax(...);
});
var d3 = d2.then(function () {
if (invalid) {
return $.Deferred().reject("Invalid");
}
return $.ajax(...);
});

d3.done(function () {
// This is called when we're all done.
});

Which could be simplified to this:

$.ajax(...).then(function () {
if (invalid) {
return $.Deferred().reject("Invalid");
}
return $.ajax(...);
}).then(function () {
if (invalid) {
return $.Deferred().reject("Invalid");
}
return $.ajax(...);
}).done(function () {
// This is called when we're all done.
})
// Use this to handle failure. You can also add to individual
// deferred objects to handle each failure differently.
.fail(function () {
// Do something.
});

To handle the failures, you may need to add another callback inside the then:

.then(function () {
if (invalid) {
return $.Deferred().reject("Invalid");
}
// Do stuff
}, function (err) {

})


Related Topics



Leave a reply



Submit