jQuery AJAX Race Condition [duplicate] - javascript

This question already has answers here:
How do I return the response from an asynchronous call?
(41 answers)
Closed 7 years ago.
I've seen similar questions asked on SO but no quite my problem.
I have something like
var x = 5;
if( some condition ){
$.ajax({
url:"...",
success: function(response){
x = response.x;
}
}
}
some other logic
use x variable
What is happening is the thread executing the JS gets to the logic that uses x before the asynchronous task comes back.
Normally I would just put whatever logic inside the success function but I need the logic to happen regardless of whether or not the condition is met and I don't want to replicate code. Anyone have any suggestions? Should I just async to be false?
Thanks!

async false would be one way to go about it since you want the value returned by x to be used if the condition is true.
You could also use promises.
var x = 5;
var ajaxCall;
if(some condition) {
ajaxCall = $.ajax({
url: "...";
});
}
function usingx(x) {
//Add your logic
}
if(typeof(ajaxCall) === 'undefined') {
usingx(x);
} else {
ajaxCall.done(function(data){
usingx(data.x);
});
}
It is either going to async false or you have call the function in two cases, one case where it can execute without waiting and the other in which it has to wait.
A bit more complicated way would be to wait till Ajax gets completed and then run the function.
var x = 5;
var isCallMade = false;
if(some condition) {
isCallMade = true;
ajaxCall = $.ajax({
url: "...";
}).done(function(data){
x = data.x;
}).always(function(){
isCallMade = false;
});
}
function useX() {
if(isCallMade) {
setTimeout(useX, 100); //Wait for ajax call to finish
} else {
//logic using x
}
}

Use the always callback.
if( some condition ){
$.ajax({
url:"...",
success: function(){ // BTW, as of jQuery 1.8 this is referred to as "done"
// Success specific code
},
always: function() {
// Code that always executes regardless of success or failure
}
}
}

JQuery also has an fail() callback in case the Ajax fails. I'd just put the logic in both cases. (abstracted into a function)
Check out this question.
jQuery: Handle fallback for failed AJAX Request
**Revision
$.ajax's complete() method runs after an ajax call no matter what. Sounds perfect.
http://api.jquery.com/jquery.ajax/

Related

Using setTimeout and recursion to see if something is "ready"?

I'm trying to figure out a good way to check if some asynchronous call is "ready" or not. I have some function that runs $.ajax, and in the callback function sets a boolean variable in the global scope to true (along with some other stuff). Before the ajax call, that boolean variable is false.
I want another function that retrieves that "other stuff." Since the ajax call is asynchronous, clearly I can't just immediately go retrieving it because it probably won't be there yet. That's where this boolean variable comes in. I'm thinking I can just check if that boolean is true every 100ms, and when it is, THEN go retrieve and return the "other stuff".
Code-wise, it looks something like this:
window.FOO = window.FOO || {};
;(function() {
var isReady = false;
var stuff;
$.ajax({
...
success: function(data) {
stuff = data;
isReady = true;
}
})
FOO.getStuff = function() {
// How to check if it's "ready"?
};
}
... (somewhere else)...
var stuff = FOO.getStuff();
I've tried the following for FOO.getStuff to no avail, because (I think) setTimeout is asynchronous:
FOO.getStuff = function() {
if (isReady) {
return stuff;
}
var theStuff;
setTimeout(function() {
theStuff = FOO.getStuff();
}, 100);
return theStuff;
};
Using the console, I can see it doing the right thing... but the first FOO.getStuff call returns before the subsequent ones do.
Is there a better way of doing this?
Edit: To clarify, I want the ajax call to remain asynchronous. I'm perfectly fine with the getStuff() calls being synchronous, because the ajax call will be very fast, and in most cases, getStuff() will be called later (after the used does some things).
Per your comments I have your answer. To solve async problem we should do async actions.
var stuff;
var stuffQueue = [];
$.ajax({
success: function(data) {
stuff = data;
if( stuffQueue.length > 0 ){
for(var i in stuffQueue){
var callback = stuffQueue[i];
callback(stuff);
}
stuffQueue = [];
}
}
});
function getStuff(callback){
//stuff has been loaded?
if( stuff ){
callback(stuff);
}else{
stuffQueue.push(callback);
}
}
To get stuff invoke:
var something = getStuff(function(stuff){
console.log(stuff);
});
This should solve your use case. Let me tell you more info, I have a JavaScript template engine, not yet open source but I have been using in professional projects, and I load all the templates with just one HTTP request, I made that request async:false because it is a dependence of the project.
There are reads that said that async false is evil, I do not believe so, what is evil is to use it wrong. Loading a templates file master, is a good example where async:false could work.
Additional I recommend you to read about promisses:
http://www.html5rocks.com/en/tutorials/es6/promises/
similar idea with Danie Aranda, I'd like to sugget you use custom event.
var isReady = true;
$.ajax({
beforeSend: function() {
isReady = false;
},
success: function(data) {
isReady = true;
$(document).trigger('display-stuff', data);
}
});
Foo.getStuff = function(data) {
if (!isReady) {
$(document).one('display-stuff', Foo.getStuff);
return;
}
// do something
};

Execute a function when a series of jquery ajax calls complete(!)

I need to make a series (1-20) ajax calls and I need to have another function be called when they are all complete. And by complete I mean when $.ajax({ complete: ...}) gets called.
Iv looked into using $.when, but after fiddling with it on jsfiddle I have 2 issues with it. 1. $.then and $.done gets called before all my complete callbacks.
2. if one single ajax call fails, it wont get called at all.
basically, it seems $.done is called on success, and not complete.
Im thinking there must be some good ajax manager/queue thingy out there that can handle this kind of stuff. Or maybe even some generic async task hanlding thingy.. ;)
The fiddle: http://jsfiddle.net/zvSgX/2
If you don't like the default jQuery choices for when $.ajax
calls resolve their deferred objects, you could write your own wrapper;
var my_ajax = function (options) {
var deferred = $.Deferred();
var user_complete = options.complete;
options.complete = function (jqXHR, textStatus) {
if (user_complete) user_complete(jqXHR, textStatus);
deferred.resolve();
};
$.ajax(options);
return deferred.promise();
};
Here is a fork of your JSFiddle which demos it in action with your
sample code.
Since my_ajax does not ever call deferred.reject(), chaining a .fail to $.when
will be meaningless if all the arguments to $.when are my_ajax calls.
Hope that is helpful! Please let me know if I can clarify anything.
You can use promise pattern to solve this problem
You can use when.js library to resolve this type of problem
Tutorial and samples are available at below location
https://github.com/cujojs/when
a solution I used recently worked not bad imo. Going through a for-loop I called a method which in turn did a window.setTimeout with a function executing an ajax call with the right data. I used a max and counter variable to check if all ajax calls where executed right (increment counter at the end of the success function). A function called by another setTimeout checked if the counter was equal to max. If not, call the method again in a new setTimeout, otherwise call the function that must be executed at the end.
So in code:
var count = 0, max = 0;
function batchCall() {
var a = [{
method: "DoThis",
params: { param1: 1, param2: 2 }
}, {
method: "DoThat",
params: { param1: 3 }
}]
max = a.length;
for (var i = 0; i < max; i++) {
callAjax(a[i]);
}
window.setTimeout(checkAllFinished, 100);
}
function callAjax(o) {
window.setTimeout(function() {
// do ajax call here
}, 0);
}
function checkAllFinished() {
if (count == max) {
// do what you need to do when all are called
}
else {
window.setTimeout(checkAllFinished, 100);
}
}

jquery execute function when two conditions are met

I need to execute a specific function mvFinishItUp() when two conditions are met. More specifically, one condition is the callback success of a $.ajax the other is a normal flow of the code until it reaches the function. Kinda of this:
$.ajax({
url: mv_finalUrl,
success: function (data) {
mvFinishItUp(data);
},
dataType: 'html'
});
/* here a lot more code, with animations and some delays */
mvFinishItUp(data) {
/* My function code */
/* But this code must only run after it has been called via the call back
and after all the other code has been ran as well */
}
So, the function must wait for all the code if the ajax callback is quicker, or the other way around. Any ideas on how this could be implemented?
I'm willing to change the whole concept of script code, as I believe the loose code between the ajax, and the function itself should go to a function aswell ...
This is a perfect use case for jQuery Deferred objects.
Remove the success: parameter from the AJAX call, and register the handler later:
var jqxhr = $.ajax(...);
// do some other synchronous stuff
...
// and *then* register the callback
jqxhr.done(mvFinishItUp);
Deferred objects cope perfectly well (by design) with being registered on an AJAX event after that event already finished.
Try like below, (It is just psuedo code)
var isAJAXDone = false, isFunctionCodeDone = false;
$.ajax({
//..
success: function () {
isAJAXDone = true;
mvFinishItUp(data, isAJAXDone, isFunctionCodeDone);
}
});
//..Your function code
//..Add this below the last line before the function ends
isFunctionCodeDone = true;
mvFinishItUp(data, isAJAXDone, isFunctionCodeDone);
//..
mvFinishItUp(data, isAJAXDone, isFunctionCodeDone ) {
if (isAJAXDone && isFunctionCodeDone) {
//Do your magic
}
}
Maybe something like this would do the trick:
var _data = undefined;
$.ajax({
url: mv_finalUrl,
success: function (data) {
_data = data;
myFinishItUp(data); // call the function from here if it's faster
},
dataType: 'html'
});
/* here a lot more code, with animations and some delays */
function myFinishItUp(data) {
this.data = data; // store the data from the AJAX call or the code, whichever reaches first
// if the code reaches this before the AJAX call completes, data will be undefined
if(typeof this.wasCalled == "undefined") {
/* My function code */
/* But this code must only run after it has been called via the call back
and after all the other code has been ran as well */
this.wasCalled = true;
}
}(_data); // function that calls itself when the code gets to this point with a self-contained boolean variable to keep track of whether it has already been called
I used a self calling function execute when the code flow gets to that point, but if it's called from the AJAX call, it won't execute. It keeps track of whether or not it's already been called with a self-contained boolean value.
Here I add an second parameter to check callback check
function mvFinishItUp(data, byCallback) {
var iscallback = byCallback || false; // if you don't send byCallback
// default will false
if(iscallback) {
// execute if called by callback
}
}
success: function (data) {
mvFinishItUp(data, true); // call with second parameter true
},
To execute mvFinishItUp() after ajax done and all codes between ajax and mvFinishItUp finished you can do something like this:
var allFunctionExecuted = false; // global to detect all code execution
$.ajax({
url: mv_finalUrl,
success: function (data) {
mvFinishItUp(data, true);
},
dataType: 'html'
});
function func1() {
}
function func2() {
}
// some other code
function func3() {
allFunctionExecuted = true;
}
Now,
function mvFinishItUp(data, byCallback) {
var iscallback = byCallback || false; // if you don't send byCallback
// default will false
if(iscallback && allFunctionExecuted) {
// execute if ajax done
// and others code done
}
}
This is very "ugly" code, but you can modify it to not use global vars, so this is just illustrative:
var ajaxExecuted = false,
codeExecuted = false;
$.ajax({
url: mv_finalUrl,
success: function (data) {
ajaxExecuted = true;
mvFinishItUp(data);
},
dataType: 'html'
});
/* here a lot more code, with animations and some delays */
codeExecuted = true;
mvFinishItUp(data) {
/* My function code */
if(ajaxExecuted && codeExecuted) {
/* But this code must only run after it has been called via the call back
and after all the other code has been ran as well */
}
}
I just added two flags: ajaxExecuted and codeExecuted, and inside the function an if statement that checks the value of the those flags, and executes only when the two of them are set to true. So no mather who calls it first, it get only executed when the two flags are set to true.
A cleaner way could be to implement the function in an object, and use properties instead of global vars.

jQuery Ajax How do callbacks work?

Hello fellow programmers! I just started an additional programming project and swore to god my code will bo SO much cleaner and easily upgradeable than it has been before.
Then I stumbled upon my "arch enemy" the jQuery AJAX returning. Last time I wanted to return something from an AJAX call I had to bend over and just make the call synchronous. That made things sticky and ugly and I hope that this time I will find something better.
So I have been googling/searching stackoverflow for a while now, and just don't understand this solution many ppl has gotten which is called callback function. Could someone give me an example on how I could exploit these callback functions in order to return my login statuses:
function doLogin(username, password) {
$.ajax({
url: 'jose.php?do=login&user='+username+'&pass='+password,
dataType: 'json',
success: function(data) {
if(data.success==1) {
return('1');
} else {
return('2');
}
$('#spinner').hide();
},
statusCode: {
403:function() {
LogStatus('Slavefile error: Forbidden. Aborting.');
$('#spinner').hide();
return (3);
},
404:function() {
LogStatus('Slavefile was not found. Aborting.');
$('#spinner').hide();
return (3);
},
500:function() {
LogStatus('Slavefile error: Internal server error. Aborting.');
$('#spinner').hide();
return (3);
},
501:function() {
LogStatus('Slavefile error: Not implemented. Aborting.');
$('#spinner').hide();
return (3);
}
},
async: true
});
}
So as you probably know, you cannot use return the way I have done from inside an AJAX call. You should instead use callback functions which I have no idea of how to use.
I'd be VERY greatful if someone could write me this code using callback functions and explaining to me just HOW they WORK.
EDIT:
I REALLY need to return stuff, not use it right away. This function is being called from within another function and should be able to be called from different places without being rewritten even slightly.
/EDIT
Sincerly,
Akke
Web Developer at Oy Aimo Latvala Ab
There are three parts to the basic "I need an asynchronous callback" pattern:
Give the function a callback function parameter.
Call the callback function instead of returning a value.
Instead of calling the function and doing something with its return value, the return value will be passed to your callback function as a parameter.
Suppose your synchronous mind wants to do this:
function doLogin(username, password) {
// ...
return something;
}
switch(doLogin(u, p)) {
case '1':
//...
break;
case '2':
//...
break;
//...
}
but doLogin has to make an asynchronous call to a remote server. You'd just need to rearrange things a little bit like this:
function doLogin(username, password, callback) {
return $.ajax({
// ...
success: function(data) {
if(data.success == 1)
callback('1');
else
callback('2');
},
//...
});
}
var jqxhr = doLogin(u, p, function(statusCode) {
switch(statusCode)) {
case '1':
//...
break;
case '2':
//...
break;
//...
}
});
The jqxhr allows you to reference the AJAX connection before it returns, you'd use it if you needed to cancel the call, attach extra handlers, etc.
A callback is simply a function that runs when certain conditions are met. In this case, it is when ajax has a "success".
You are already using a callback, but you don't recognize it. success: function(data) {} is a callback, but it's just what's called an anonymous function. It has no name or reference, but it still runs. If you want to change this anonymous function to a named function, it is really simple: take the code in the anonymous function, and put it in a named one, and then just call the named one:
[...]success: function(data) {
if(data.success==1) {
return('1');
} else {
return('2');
}
$('#spinner').hide();
}, [...]
should change to:
[...]success: function(){ callbackThingy(data) }, [...]
And now just create the callbackThingy function:
function callbackThingy(data){
if(data.success==1) {
someOtherFunction('1');
} else {
someOtherFunction('2');
}
$('#spinner').hide();
}
Note that the "return" value does nothing. It just stops the callback function, whether you are in an anonymous function or a named one. So you would also have to write a second function called someOtherFunction:
function someOtherFunction(inValue){
if(inValue=='1') {
// do something.
} else if(inValue=='2') {
// do something else.
}
}
The above example is if you have to pass parameters. If you do not need to pass parameters, the setup is simpler:
[...]success: callbackThingy, [...]
function callbackThingy(){
// do something here.
}
From the edit in your original post, I can see that you just need to store a (more) global variable. Try this:
// in the global scope , create this variable:
// (or -- at least -- in the scope available to both this ajax call
// and where you are going to use it)
var valHolder = -1;
// then edit your ajax call like this:
[...]
success: function(data) {
if(data.success==1) {
valHolder = 1;
} else {
valHolder = 2;
}
$('#spinner').hide();
},
[...]
Now you can verify 3 things:
valHolder = -1 means that the ajax call has not yet returned successfully
valHolder = 1 means data.success = 1
valHolder = 2 means data.success != 1.
Another option is to store the variable in an HTML attribute of some element.
Finally, you should probably look at jquery.data for the most jquery way of managing stored data.
Does this help?
Just as a small point of interest, you don't have to include
async : true;
as part of your $.ajax options. The default setting for async is already "true".
Sorry to post this as a response, but until I have 50 rep I can't make a simple comment. (Feel free to help me out with that! ^_^ )

JavaScript asynchronous return value / assignment with jQuery [duplicate]

This question already has answers here:
How do I return the response from an asynchronous call?
(41 answers)
Closed 9 years ago.
I have the following jQuery Function. I'm trying to return the GUID value shown here in the alert(); The alert works fine and the value is populated, however I can't seem to assign it to a variable and return its value.
Ultimately I need to access the GUID value in other functions, etc. Everything I've tried only displays as undefined.
I'd like to do something like this:
function trackPage(){
var elqTracker = new jQuery.elq(459);
elqTracker.pageTrack({
success: function() {
elqTracker.getGUID(function(guid) {
alert(guid);
var returnValue = guid;
});
}
});
return returnValue;
}
var someGuid = trackPage();
So, this question has been asked a million times over, and I'm sure that everyone (myself included) tried this once.
It is just the nature of an asynchronous call, you can't use their results as a return value. Thats why they have you passing in a function that gets the result of the call, they can't return it either! Also notice that the elqTracker.pageTrack() function call returns IMMEDIATELY, therefore your returnValue is simply undefined.
Most people (see dfsq's answer) solve this problem by introducing a callback function as a paramater. This method is tried, and true – however jQuery has $.Deferred. This allows you to make your own asynchronous logic return a promise which you can then attach any number of callbacks to:
function trackPage(){
var elqTracker = new jQuery.elq( 459 ),
dfd = $.Deferred();
elqTracker.pageTrack({
success: function() {
elqTracker.getGUID(function( guid ) {
dfd.resolve( guid );
});
}
});
return dfd.promise();
}
// example use:
trackPage().done(function( guid ) {
alert( "Got GUID:" + guid );
});
Notice now that your trackPage() returns an object that you can attach callbacks to? You don't have to attach them immediately either.
var pageHit = trackPage().done(function( guid ) {
alert( "Page Hit GUID:" +guid );
});
$("button").click(function() {
pageHit.done( function( guid ) {
alert( "Clicked on Page GUID:" + guid );
});
});
Also, the jQuery AJAX module always returns promises as well, so the interface for all your AJAX stuff should be very similar if you make your own logic return promises.
As a side note: I'd like to point out that your var returnValue was in the wrong "scope" anyway. It needed to be declared in the outer scope of the trackPage function. Even with this fix, the concept still doesn't work.
Since you have asynchronous call the way you are trying to write code is not going to work (because by the moment of return returnValue; in the trackCode return value is not yet defined). Instead you should pass callback into trackPage:
function trackPage(callback) {
var elqTracker = new jQuery.elq(459);
elqTracker.pageTrack({
success: function() {
elqTracker.getGUID(function(guid) {
alert(guid);
// Instead of this: var returnValue = guid;
// You should use your callback function
callback(guid);
});
}
});
return returnValue;
}
trackCode(function(guid) {
// perform some actions with guid
});

Categories

Resources