jQuery Ajax How do callbacks work? - javascript

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! ^_^ )

Related

Better way to alternate actions after callback in JavaScript

I have some API (LoadFileApi.load) for load file that fires callback on complete. There is common logic (prepare) and two different ways of processing (fired by click handlers) that are triggered upon prepare. I wonder, how to make it in clear and convenient, JS way?
function loadFile (onCompleteFileLoad) {
LoadFileApi.load({
url: 'url',
onComplete: filePreparing
});
}
function fileProcessing1() {
}
function fileProcessing2() {
}
// common logic
function filePreparing(file) {
// prepare
...
// after prepare I need to run file processing routine corresponding to each handler
}
function clickHandlerA() {
loadFile(filePreparing);
// needs to trigger fileProcessing1
}
function clickHandlerB() {
loadFile(filePreparing);
// needs to trigger fileProcessing2
}
Simple decision is to declare shared variable and set it in each handler before file load.
function filePreparing(file) {
// prepare
...
if (processingMethod == 1) {
fileProcessing1();
} else {
fileProcessing2();
}
}
function clickHandlerA() {
processingMethod = 1;
loadFile(filePreparing);
}
function clickHandlerB() {
processingMethod = 2;
loadFile(filePreparing);
}
var processingMethod;
But it seems to me that more elegant JS way should exists... It would be great if there is no conditional choosing of processing function in PREPARE. It should be passed as parameter somehow...
I believe you want something similar to the answer to this question - JavaScript: Passing parameters to a callback function
Using the pattern described in the answer in the link above you will see that you can simply pass in parameters into your callback generically.
something like:
function clickHandlerB() {
loadFile(filePreparing, fileProcessing1);
}
instead of passing a string flag to indicate which function to call just pass the function itself. The filePreparing function's signature will need to be updated to include the callback function
function filePreparing(file, callback)

Return value of a callback function in jQuery?

I have this code:
function dialogTexts(message) {
var langText = $.ajax({
type: 'GET',
url: '/index.php/main/dialogtexts',
dataType: 'json'
});
langText.done(function(data) {
message(data);
});
langText.fail(function(ts) {
alert(ts.responseText);
});
}
dialogTexts(function(text) {
alert(text.delete);
});
I'm using a callback to alert the string text.delete (in above code), but I want to do something like:
dialogTexts(function(text) {
return text;
});
var deleteConfirmationDialogText = dialogTexts.delete;
alert(deleteConfirmationDialogText); //deleteConfirmationDialogText is undefined
dialogTexts is a function and it does not have a property .delete. That's why dialogTexts.delete is undefined when you try to use it. You simply can't use the kind of shortcut you're trying to do.
Also, the return statement in here:
dialogTexts(function(text) {
return text;
});
is doing nothing. It's just returning to the caller of that callback which is inside of dialogTexts() and that caller is not doing anything with the return value so return text is doing nothing.
If you want to alert the text, you will have to do what you first had:
dialogTexts(function(text) {
alert(text.delete);
});
That is the only real way to do it and this is a consequence of how asynchronous programming works with AJAX calls. There are some other techniques for handling callbacks (such as promises), but they all require you to process the asynchronously returned data in a callback function. That's just the nature of asynchronous programming.

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.

AJAX ResponseText as DOM?

Consider the following function using jQuery:
function getVal() {
jQuery.get('/relative/url/', function (data) {
return data.getElementById('myInput').value;
}
}
This is basically what I want to do, but I have no idea how it should be done.
The only methods I know would work involve frames or innerHTML which I can't use because I have to wait for the element to be ready. The only way to do that is to use a callback, and this function must return the value of the element rather than something else.
My logic is likely faulty here, so please feel free to correct me.
First of all, with your current structure you should use a callback to return the value. To parse the HTML string retrieved via AJAX, you can hand it to jQuery and then query it just as usual.
function getVal(callback) {
jQuery.get('/relative/url/', function (data) {
callback($(data).find('#myInput').val());
}, 'html');
}
Then, when you are calling the function getVal, you'll need to provide a callback:
getVal(function(input_val) {
/**
* This code will be run once the GET request finishes.
* It will be passed one parameter - the value of #myInput in the HTML
* response to the request (see getVal function).
*/
alert(input_val);
});
No, you could not do that.. since it is ansync call. What you need is to provide a callback to you code, to return the value
function getVal(callback) {
jQuery.get('/relative/url/', function (data) {
callback(data.getElementById('myInput').value);
}
}
getVal(function (value) {
alert(value);
});
if the it's valid html markup, you can use browse its xml with the selector:
*[id=myInput]
or you can just render the markup on some dummy element in your page and then do you lookup:
function getVal() {
jQuery.get('/relative/url/', function (data) {
dummyNode.innerHTML = data; //not sure data points to responseTxt
return getElementById('myInput').innerHTML;
}
}
You cannot do that unless the elements are added to dom tree.
function getVal() {
jQuery.get('/relative/url/', function (data) {
return $(document.body).append(data).find('#myInput').val();
}
}
Several problems there. First, you cannot return from a callback like that. You would just return to the anonymous function itself, not from the getVal() method.
To solve that, you can return the jXHR object and apply some magic:
function getVal() {
return jQuery.get('/relative/url/');
}
getVal().done(function (data) {
var val = $( data ).find('#myInput').val();
// do something with val
}
I don't know how the structure from data looks like, but it should work that way. If not, its probably because of myInput is on the top level. In that case, replace .find() with .filter().
Oh, alright. I've got it. I don't think I provided enough information. I assumed context was irrelevant. Alright, here's an example depicting my solution.
function getVal() {
$.get('/relative/url/', function (data) {
$.get('relative/url/2', function (data2) {
var data_obj = [];
data_obj.push({
"data_1":data[i].name
}, {
"data_2":$(data).find('#myInput').val()
});
});
}
}

Categories

Resources