I am wanting an image to change to a loading symbol when clicked, and then change back when the AJAX functions are finished.
I've got the below, however it is not changing at all (well, it is changing, but immediately changing back without waiting for the ajax call to finish). Why is this happening? Is this because my ajax call is enclosed in a function?
//adding product to cart
$("body").on("click", "#cart_product_add", function() {
var clickedIcon = $(this);
//show the spinning loader
var loader = $(clickedIcon).attr("src").replace("add_icon.png", "green_loading.gif");
$(clickedIcon).attr("src", loader);
var code = $(clickedIcon).data("product_code");
var description = $(clickedIcon).data("product_description");
var whqc = $(clickedIcon).data("product_whqc");
var qty = $(clickedIcon).prev("input[name=qty]").val();
$.when(
addCartProduct($("input[name=cart_id]").val(), code, description, whqc, qty)
).then(function() {
loader = $(clickedIcon).attr("src").replace("green_loading.gif", "add_icon.png");
$(clickedIcon).attr("src", loader);
});
});
$.when() can only do its job if you pass it one or more promises. It doesn't have any magic ability to know when an ajax call inside the function you pass it is done. So, if you don't pass it a promise, it just executes then .then() handler immediately and doesn't wait for anything (which is what you're seeing).
Since jQuery Ajax calls already return a promise, you probably can just return that promise. And, if you only have one operation you're waiting for, there's no reason to use $.when either. Here's a skeleton for how you could do it:
addCartProduct($("input[name=cart_id]").val(), code, description, whqc, qty).then(function() {
loader = $(clickedIcon).attr("src").replace("green_loading.gif", "add_icon.png");
$(clickedIcon).attr("src", loader);
});
function addCartProduct() {
return $.ajax(...);
}
This could help you
http://api.jquery.com/ajaxstart/
you can specify this once and it triggers for every ajax request.
Related
I have an issue with js promises that I hope someone can help me with. The function myFunction below performs a $.ajax call and therefore returns a promise. As I need to hand control back to the browser in order to show the refreshed div that this function updates as I recurse, I'm calling a setTimeout as follows:
var nextBitOfWork = function () {
return myFunction(email);
};
setTimeout(nextBitOfWork, 0);
where myFunction (which recurses) now returns a promise when it's done doing it's $.ajax call.
If I simply call:
return myFunction(email);
without the setTimeout function construct above, the promise is passed through and all my promises are captured and allow me to get the array output I need and everything works great when recursion ends. But without the setTimeout I don't get the browser refresh. Using it as above I get the div update refresh displaying, but seem to lose the promise and so the script continues and I don't get to fill the array that myFunction builds as it recurses.
Any thoughts on how to make sure the setTimeout passes on the promise reliably so that I build the response array and display the div updates as I do so?
Thanks, in advance, for your help!
OK - now have the following:
var func = function () {
myFunction(email);
};
return refreshscreen(func,0);
where refreshscreen is:
function refreshscreen(func,time) {
var timer = $.Deferred();
setTimeout(function () {
return func().then(timer.resolve());
}, time);
return timer.promise();
}
Still the same issue - although the browser renders the div, the array I build with myFunction for the collected $.ajax responses is only 1 element in length - though it recurses 20 times! Without the call to refresh screen, the array builds fine, but the browser never renders the div's as we recurse!
setTimeout does not return the value returned by the function that you pass in. (It returns a value you can use to stop the timeout by passing it in to clearTimeout)
So in order to receive the returned value from myFunction just wrap it in a function in your call to setTimeout.
setTimeout(function () {
var promise = myFunction(email);
// do something with promise...
}, 0);
Wondering what the best solution to this problem is, also this is not my actual code structure or names but the simplest way to illustrate the problem.
I have a function which was purely used to perform an ajax call and load a template with jquery.
function load(template) {
$('#container').load(template, data, function() {
// complete code here
});
}
Focusing on the 3rd param in $.load(), namely a callback function that runs when the request is complete.
Now I have my load() function in another wrapper function:
function processTask(variable) {
load(variable);
}
The problem I have is I need some code to run after the ajax load is complete, however as my app has grown my wrapper function processTask may or may not invoke an ajax load so I can't perform my must needed code inside the complete callback.
Do I change my $.load() to perform synchronous or just manage my code better so that if I am calling a $.load() it puts my needed code in the callback and if not it places it where I need it to be?
I have read about javascript Promises and I'm unsure if they will help in this situation.
EDIT
So my processTask is an object method.
function classObj(name, fn) {
this.name = name;
this.processTask = fn;
this.load = function(template) {
$('#container').load(template, data, function() {
// complete code here
});
}
}
And in context I do this:
var task = new classObj('taskName', function() {
this.load('myFile.php');
// Or another function and not load() based on whats needed in the task.
});
Basically I have an object that I can add custom methods to at will and they can easily be called dynamically, until now they have always loaded a file.
First, change your load function to return the xhr from get (or ajax):
function load(template) {
return $.get('myFile.php', data, function(result) {
$('#container').html(result);
});
}
Then, within your code you can use when then to perform your code after the load completes if applicable:
var xhr;
/* ... */
if(something){
xhr = load(template);
}
/* ... */
if(xhr){
$.when(xhr).then(doSomething);
} else {
doSomething();
}
And in fact, this can be simplified using the fact that a non-deferred object passed to when (including undefined apparently) will execute the then immediately and get rid of the if:
$.when(xhr).then(doSomething);
If xhr is undefined then when will resolve immediately causing then to execute immediately.
I'm having an issue that is burning my head. Basically this is the scenario:
I have a callController() function, which just simple use the jQuery.load() method to load a controller, after loaded, I can play with the returning parameters.
Now... in one of my controllers there is a validation rule I need to check in order to to allow the user to execute certain functionality, however, I create a function like this:
function myValRule() {
var val = callController('blablabla'),
response = "";
val.done(function(data) {
//my business logic
response = something;
}
return response;
}
As you imagine, response, even if It has a value, it returns undefined, so I set a timeout and console.log() it and now it has a value, but I cannot make to return this value even if I put the return into the setTimeout(). So basically when the method call this function to validate it finds it empty.
Can you point me on some direction to solve this issue?
Remember this is asynchronous! The return response; is long gone by the time .done() is actually called.
if you need the value returned in this fashion, look at either supplying a callback function in your myValRule function (that would be called from within .done()) or maybe look at using the $.Deferred api so you can mimic what callController(...) is doing.
Here's an example of both scenarios (with example call to myValRule):
Callback argument
function myValRule(callback){
var val = callController('blablabla'),
val.done(function(data){
var response = /* something */;
callback(response);
});
}
myValRule(function(response){
// here you have response
});
Using $.Deferred
(I assume it's jQuery since there's a .done call)
function myValRule(){
var df = $.Deferred(),
val = callController('blablabla');
val.done(function(data){
var response = /*something */;
df.resolve(response);
}).fail(df.reject);
return df.promise();
}
myValRule().done(function(response){
// here you have response
});
So, I have a page that loads and through jquery.get makes several requests to populate drop downs with their values.
$(function() {
LoadCategories($('#Category'));
LoadPositions($('#Position'));
LoadDepartments($('#Department'));
LoadContact();
};
It then calls LoadContact(); Which does another call, and when it returns it populates all the fields on the form. The problem is that often, the dropdowns aren't all populated, and thus, it can't set them to the correct value.
What I need to be able to do, is somehow have LoadContact only execute once the other methods are complete and callbacks done executing.
But, I don't want to have to put a bunch of flags in the end of the drop down population callbacks, that I then check, and have to have a recursive setTimeout call checking, prior to calling LoadContact();
Is there something in jQuery that allows me to say, "Execute this, when all of these are done."?
More Info
I am thinking something along these lines
$().executeAfter(
function () { // When these are done
LoadCategories($('#Category'));
LoadPositions($('#Position'));
LoadDepartments($('#Department'));
},
LoadContact // Do this
);
...it would need to keep track of the ajax calls that happen during the execution of the methods, and when they are all complete, call LoadContact;
If I knew how to intercept ajax that are being made in that function, I could probably write a jQuery extension to do this.
My Solution
;(function($) {
$.fn.executeAfter = function(methods, callback) {
var stack = [];
var trackAjaxSend = function(event, XMLHttpRequest, ajaxOptions) {
var url = ajaxOptions.url;
stack.push(url);
}
var trackAjaxComplete = function(event, XMLHttpRequest, ajaxOptions) {
var url = ajaxOptions.url;
var index = jQuery.inArray(url, stack);
if (index >= 0) {
stack.splice(index, 1);
}
if (stack.length == 0) {
callback();
$this.unbind("ajaxComplete");
}
}
var $this = $(this);
$this.ajaxSend(trackAjaxSend)
$this.ajaxComplete(trackAjaxComplete)
methods();
$this.unbind("ajaxSend");
};
})(jQuery);
This binds to the ajaxSend event while the methods are being called and keeps a list of urls (need a better unique id though) that are called. It then unbinds from ajaxSend so only the requests we care about are tracked. It also binds to ajaxComplete and removes items from the stack as they return. When the stack reaches zero, it executes our callback, and unbinds the ajaxComplete event.
You can use .ajaxStop() like this:
$(function() {
$(document).ajaxStop(function() {
$(this).unbind("ajaxStop"); //prevent running again when other calls finish
LoadContact();
});
LoadCategories($('#Category'));
LoadPositions($('#Position'));
LoadDepartments($('#Department'));
});
This will run when all current requests are finished then unbind itself so it doesn't run if future ajax calls in the page execute. Also, make sure to put it before your ajax calls, so it gets bound early enough, it's more important with .ajaxStart(), but best practice to do it with both.
Expanding on Tom Lianza's answer, $.when() is now a much better way to accomplish this than using .ajaxStop().
The only caveat is that you need to be sure the asynchronous methods you need to wait on return a Deferred object. Luckily jQuery ajax calls already do this by default. So to implement the scenario from the question, the methods that need to be waited on would look something like this:
function LoadCategories(argument){
var deferred = $.ajax({
// ajax setup
}).then(function(response){
// optional callback to handle this response
});
return deferred;
}
Then to call LoadContact() after all three ajax calls have returned and optionally executed their own individual callbacks:
// setting variables to emphasize that the functions must return deferred objects
var deferred1 = LoadCategories($('#Category'));
var deferred2 = LoadPositions($('#Position'));
var deferred3 = LoadDepartments($('#Department'));
$.when(deferred1, deferred2, deferred3).then(LoadContact);
If you're on Jquery 1.5 or later, I suspect the Deferred object is your best bet:
http://api.jquery.com/category/deferred-object/
The helper method, when, is also quite nice:
http://api.jquery.com/jQuery.when/
But, I don't want to have to put a bunch of flags in the end of the drop down population callbacks, that I then check, and have to have a recursive setTimeout call checking, prior to calling LoadContact();
No need for setTimeout. You just check in each callback that all three lists are populated (or better setup a counter, increase it in each callback and wait till it's equal to 3) and then call LoadContact from callback. Seems pretty easy to me.
ajaxStop approach might work to, I'm just not very familiar with it.
If I have an ajax call off fetching (with a callback) and then some other code running in the meantime. How can I have a third function that will be called when both of the first 2 are done. I'm sure it is easy with polling (setTimeout and then check some variables) but I'd rather a callback.
Is it possible?
You could just give the same callback to both your AJAX call and your other code running in the meantime, use a variable to track their combined progress, then link them to a callback like below:
// Each time you start a call, increment this by one
var counter = 0;
var callback = function() {
counter--;
if (counter == 0) {
// Execute code you wanted to do once both threads are finished.
}
}
Daniel's solution is the proper one. I took it and added some extra code so you don't have to think too much ;)
function createNotifier() {
var counter = 2;
return function() {
if (--counter == 0) {
// do stuff
}
};
}
var notify = createNotifier();
var later = function() {
var done = false;
// do stuff and set done to true if you're done
if (done) {
notify();
}
};
function doAjaxCall(notify) {
var ajaxCallback = function() {
// Respond to the AJAX callback here
// Notify that the Ajax callback is done
notify();
};
// Here you perform the AJAX call action
}
setInterval(later, 200);
doAjaxCall(notify);
The best approach to this is to take advantage of the fact that functions are first-order objects in JavaScript. Therefore you can assign them to variables and invoke them through the variable, changing the function that the variable refers to as needed.
For example:
function firstCallback() {
// the first thing has happened
// so when the next thing happens, we want to do stuff
callback = secondCallback;
}
function secondCallback() {
// do stuff now both things have happened
}
var callback = firstCallback;
If both your pieces of code now use the variable to call the function:
callback();
then whichever one executes first will call the firstCallback, which changes the variable to point to the secondCallback, and so that will be called by whichever executes second.
However your phrasing of the question implies that this may all be unnecessary, as it sounds like you are making an Ajax request and then continuing processing. As JavaScript interpreters are single-threaded, the Ajax callback will never be executed until the main body of code that made the request has finished executing anyway, even if that is long after the response has been received.
In case that isn't your situation, I've created a working example on my site; view the source to see the code (just before the </body> tag). It makes a request which is delayed by the server for a couple of seconds, then a request which receives an immediate response. The second request's response is handled by one function, and the first request's response is later handled by a different function, as the request that received a response first has changed the callback variable to refer to the second function.
You are talking about a thing called deferred in javascript as #Chris Conway mentioned above. Similarly jQuery also has Deferred since v1.5.
Check these Deferred.when() or deferred.done()
Don't forget to check jQuery doc.
But to give you some idea here is what I am copying from that site.
$.when($.ajax("/page1.php"), $.ajax("/page2.php")).done(function(a1, a2){
/* a1 and a2 are arguments resolved for the
page1 and page2 ajax requests, respectively */
var jqXHR = a1[2]; /* arguments are [ "success", statusText, jqXHR ] */
if ( /Whip It/.test(jqXHR.responseText) ) {
alert("First page has 'Whip It' somewhere.");
}
});
//Using deferred.then()
$.when($.ajax("/page1.php"), $.ajax("/page2.php"))
.then(myFunc, myFailure);
Something like this (schematic):
registerThread() {
counter++;
}
unregisterThread() {
if (--counter == 0) fireEvent('some_user_event');
}
eventHandler_for_some_user_event() {
do_stuff();
}
You can do this easily with Google's Closure library, specifically goog.async.Deferred:
// Deferred is a container for an incomplete computation.
var ajaxFinished = goog.async.Deferred();
// ajaxCall is the asynchronous function we're calling.
ajaxCall( //args...,
function() { // callback
// Process the results...
ajaxFinished.callback(); // Signal completion
}
);
// Do other stuff...
// Wait for the callback completion before proceeding
goog.async.when(ajaxFinished, function() {
// Do the rest of the stuff...
});
You can join multiple asynchronous computations using awaitDeferred, chainDeferred, or goog.async.DeferredList.