I am writing a generic function that will be reused in multiple places in my script.
The function uses ajax (using jQuery library) so I want to somehow pass in a function (or lines of code) into this function to execute when ajax is complete.
I believe this needs to be a callback function, but after reading through a few callback answers I'm still a bit confused about how I would implement in my case.
My current function is:
function getNewENumber(parentENumber){
$.ajax({
type: "POST",
url: "get_new_e_number.php",
data: {project_number: projectNumber, parent_number: parentENumber},
success: function(returnValue){
console.log(returnValue);
return returnValue; //with return value excecute code
},
error: function(request,error) {
alert('An error occurred attempting to get new e-number');
// console.log(request, error);
}
});
}
With this function I want to be able to do something in the same way other jQuery functions work ie;
var parentENumber = E1-3;
getNewENumber(parentENumber, function(){
alert(//the number that is returned by getNewENumber);
});
Just give getNewENumber another parameter for the function, then use that as the callback.
// receive a function -----------------v
function getNewENumber( parentENumber, cb_func ){
$.ajax({
type: "POST",
url: "get_new_e_number.php",
data: {project_number: projectNumber, parent_number: parentENumber},
// ------v-------use it as the callback function
success: cb_func,
error: function(request,error) {
alert('An error occurred attempting to get new e-number');
// console.log(request, error);
}
});
}
var parentENumber = E1-3;
getNewENumber(parentENumber, function( returnValue ){
alert( returnValue );
});
#patrick dw's anwser is correct. But if you want to keep calling the console.log (or any other actions) always, no matter what the caller code function does, then you can add the callback (your new parameter) inside the success function you already have:
function getNewENumber(parentENumber, cb_func /* <--new param is here*/){
$.ajax({
type: "POST",
url: "get_new_e_number.php",
data: {project_number: projectNumber, parent_number: parentENumber},
success: function(returnValue){
console.log(returnValue);
cb_func(returnValue); // cb_func is called when returnValue is ready.
},
error: function(request,error) {
alert('An error occurred attempting to get new e-number');
// console.log(request, error);
}
});
}
And the calling code remains the same as yours except that the function will receive the returnValue by parameter:
var parentENumber = E1-3;
getNewENumber(parentENumber, function(val /* <--new param is here*/){
alert(val);
});
This would be better done with jQuery's Deferred Objects. Have your AJAX call return the jqXHR object.
function getNewENumber(parentENumber) {
return $.ajax( { ... } );
}
getNewENumber(E1 - 3).then(success_callback, error_callback);
If you want to keep the error callback within that function you can register that there instead:
function getNewENumber(parentENumber) {
var jqXHR = $.ajax( { ... } );
jqXHR.fail( ... );
return jqXHR;
}
getNewENumber(E1 - 3).done(success_callback);
Related
I'm trying to attempt to generalize my ajax calls into a function as follows. I have not done this before and am not sure sure if I'm doing it correctly.
<script>
$(document).ready(function(){
var reg_no=$("#reg_no").val();
reg_no=reg_no.trim();
if(reg_no!==""){
//populate fields
data={reg_no:reg_no,func:"getSupplierDetails"};
success_function="updateFormFields";
ajax_call(data,success_function);
}
});
function ajax_call(data,success_function){
$.ajax({
type:"POST",
url:"../control/supplier-c.php",
dataType:"json",
data:data,
success:function(data){
success_function(data); //variable function works??
}
});
}
function updateFormFields(data){
//some code here to handle data array
}
</script>
What I'm trying to do here is avoid rewriting the whole ajax code by passing the data array and the function to be executed on success. What I'm not sure is the use of variable functions as i have done.
A note to be made is that the whole thing works for an ajax call if updateFormFields() code was moved into the success handler in the ajax call and the ajax_call() was not defined as a seperate function but implemented right after the comment "populate fields". I just have no experience in trying it this way and I need to know if this is possible or not.
Thank You
In Javascript, functions are first class objects, meaning you can pass them around as parameters.
function json_post(url, data, success_function, error_function) {
$.ajax({
type:"POST",
url:url,
dataType:"json",
data:data
}).then(success_function, error_function);
}
Then you can call it as
json_post("../control/supplier-c.php", { data: "data" }, function (res) {
console.log('Ajax req successful');
console.log(res);
}, function (res) {
console.log('Error in ajax req');
console.log(res);
});
In your case, you can do:
ajax_call(data, updateFormFields);
and it should work as expected.
There's no need to wrap the success function, you can just apply apply it directly.
function ajax_call(data, success_function) {
$.ajax({
...
success: success_function
});
}
An even better idea is to avoid the legacy success and error callbacks and instead return the jQuery promise. You can use standard promise methods .then() and `.
function ajax_call(data) {
return $.ajax({
...
});
}
ajax_call()
.then(function(data) {
// this runs if it succeeds
})
.fail(function(err) {
// this runs if it failed
});
Promises have a huge benefit to being chain-able, making the code flatter, avoiding the nest of "christmas tree callbacks".
I would recommend checking success_function as well as failure_function to handle server response (XHR) errors also.
function success_function(){
//code to handle success callback
}
function error_function(){
//code to handle failure callback
}
function ajax_call(data, success_function, error_function) {
if (typeof success_function === 'function' && typeof error_function === 'function') {
$.ajax({
type: "POST",
url: "../control/supplier-c.php",
dataType: "json",
data: data,
}).then(success_function).fail(error_function);
}
}
I am looking for the best standard javascript way of monitoring when a method is completed.
I have a method used for ajax calls like this:
function rdRelatedJobs(param1,param2,param3) {
var completeURL = param1 + param2 + param3;
$.ajax({
type: "GET",
url:completeURL,
dataType: "xml",
success: function(xml){
// We parse the XML here
};
},
error: function() {
console.log("An error occurred while processing XML file.");
}
});
} // end function rdRelatedJobs
This function is called twice, queued immediately after each other. What I need to know is how to call another method once the second call to this method is complete.
Note it does not matter if the ajax request is success or failure in this case. I just need to know how to call the method once the method above is run twice.
There's a jQuery-specific answer, and a general answer.
The jQuery-specific answer is to return the result of the ajax call, which is a jqXHR object:
function rdRelatedJobs(param1,param2,param3) {
var completeURL = param1 + param2 + param3;
return $.ajax({
type: "GET",
url:completeURL,
dataType: "xml",
success: function(xml){
// We parse the XML here
},
error: function() {
console.log("An error occurred while processing XML file.");
}
});
} // end function rdRelatedJobs
...and when you're calling rdRelatedJobs, use $.when:
$.when(
rdRelatedJobs(/*...args for first call...*/),
rdRelatedJobs(/*...args for second call...*/)
).then(function() {
// both are done now
});
That works because jqXHR objects implement the jQuery Promise API, and $.when will call your callback when all of the promises you give it have been fulfilled.
The generic answer would be to use a promise library that does something similar.
Or another generic answer would be to have rdRelatedJobs call a callback when it's done, and to maintain a counter:
function rdRelatedJobs(param1,param2,param3,done) {
var completeURL = param1 + param2 + param3;
return $.ajax({
type: "GET",
url:completeURL,
dataType: "xml",
success: function(xml){
// We parse the XML here
done(xml);
},
error: function() {
console.log("An error occurred while processing XML file.");
done(null);
}
});
} // end function rdRelatedJobs
...and then:
var counter = 0;
function done(result) {
if (--counter === 0) {
// Both are done
}
}
rdRelatedJobs(/*...args for first call...*/, done);
++counter;
rdRelatedJobs(/*...args for second call...*/, done);
++counter;
That looks like a race condition, but it isn't, because there is only one main UI JavaScript thread in browsers.
outside the function you can have a `var counter`
and then inside:
$.ajax({
type: "GET",
url:completeURL,
dataType: "xml",
success: function(xml){
// We parse the XML here
counter++;
if(counter==2) bla bla
};
},
error: function() {
console.log("An error occurred while processing XML file.");
counter++;
if(counter==2) bla bla
}
});
function rdRelatedJobs(param1,param2,param3) {
var completeURL = param1 + param2 + param3;
// return an ajax promise from this function
return $.ajax({
type: "GET",
url:completeURL,
dataType: "xml",
success: function (xml) {
// We parse the XML here
},
error: function () {
console.log("An error occurred while processing XML file.");
}
});
}
// compile the promises by passing them into an array
function getPromises() {
var promises = [];
promises.push(rdRelatedJobs(1, 2, 3));
promises.push(rdRelatedJobs(3, 4, 5));
return promises;
}
// use $.when by passing in the promise array using the apply method
$.when.apply(null, getPromises()).then(function () {
// do a thing
});
You could use a library called Q and do an 'all' and use a 'then' with a success and failure callback
https://github.com/kriskowal/q
e.g:
Q.all([rdRelatedJobs(x,y,z), rdRelatedJobs(a,b,c)]).then(function () {
console.log("Data saved!");
});
Very similar to the jquery $.when the 'then' will only get called when both ajax requests have finished. You would have to alter the rdRelatedJobs to do a return just before the $.ajax so that the promise is returned from the function.
This question already has answers here:
Pass an extra argument to a callback function
(5 answers)
Closed 6 years ago.
I have this below code..
function getGrades(grading_company) {
if (grading_company == 'Not Specified') {
// Remove grades box & show condition box
showConditionBox();
} else {
// Set file to get results from..
var loadUrl = "ajax_files/get_grades.php";
// Set data string
var dataString = 'gc_id=' + grading_company;
// Set the callback function to run on success
var callback = showGradesBox;
// Run the AJAX request
runAjax(loadUrl, dataString, callback);
}
}
function runAjax(loadUrl, dataString, callback) {
jQuery.ajax({
type: 'GET',
url: loadUrl,
data: dataString,
dataType: 'html',
error: ajaxError,
success: function(response) {
callback(response);
}
});
}
Edit: Here is the function that gets called as the callback function:
function showGradesBox(response) {
// Load data into grade field
jQuery('#grade').html(response);
// Hide condition fields
jQuery('#condition').hide();
jQuery('#condition_text').hide();
// Show grade fields
jQuery('#grade_wrapper').show();
jQuery('#grade_text_wrapper').show();
}
Now if I wanted to pass the grading_company variable to the callback function as a parameter is there a way to do that without having to add it as another parameter in the runAjax call? I'm trying to keep the runAjax function open to other usage so I don't want to pass in any extra parameters; but if it can somehow be included within the callback then great.
change your callback to an anonymous function:
// Set the callback function to run on success
var callback = function () {
showGradesBox(grading_company);
};
This allows you to pass parameters to the inner function.
Edit: to allow for the ajax response:
// Set the callback function to run on success
var callback = function (response) {
showGradesBox(grading_company, response);
};
Another possibility is instead of doing dataString do dataObject then pass that object to the callback. Like so:
function getGrades(grading_company) {
if (grading_company == 'Not Specified') {
// Remove grades box & show condition box
showConditionBox();
} else {
// Set file to get results from..
var loadUrl = "ajax_files/get_grades.php";
// Set data object
var dataObject = {
'gc_id' : grading_company
/*to do multiples..
'item1' : value1,
'item2' : value2,
'etc' : etc */
}
// Set the callback function to run on success
var callback = showGradesBox;
// Run the AJAX request
runAjax(loadUrl, dataObject, callback);
}
}
function runAjax(loadUrl, dataObject, callback) {
jQuery.ajax({
type: 'GET',
url: loadUrl,
data: $.param(dataObject),
dataType: 'html',
error: ajaxError,
success: function(response) {
callback(response, dataObject);
}
});
}
Note the addition of $.param().
Then in the callback function, you should know what data you're after. If function setGrades(resp, data) { ... } was the callback, then you can access the values in setGrades
function setGrades(resp, data) {
alert( data.gc_id);
}
EDIT
After testing, I realize that $(dataObject).serialize() will not work. So I've updated to use $.param(). Please see this SO post for more info.
I have created a function called save , when i call save function, I am getting success undefined or object Object
UPDATES:
updated to get he values jqxhr object that ajax returns
function save() {
return $.ajax({
type: "POST",
url: "foo.json",
data: json_data,
contentType: 'application/json',
success: function (data, textStatus, xhr) {
$('<div id="loading">Loading...</div>').insertBefore('#form');
},
error: function (jqXHR, textStatus, errorThrown) {
}
});
}
$(document).ready(function () {
$(function () {
$("#save").click(function () {
var jqxhr = save();
alert("success " + jqxhr.success);
alert("status " + jqxhr.status);
alert("status " + jqxhr.readyState);
});
});
});
For the upteenth time.
ajax is asynchronous.
Use a callback function.
Ninja edit by OP
First of all, there is no return statement within your save function, so it works as expected by returning undefined value.
Secondly, it won't work that way. You need to return the $.ajax call (which itself, returns a jqXHR object, where you can hook in and setup code for different events. Afterall, by default an Ajax request runs asyncronously.
in save()
return $.ajax({ ...
and later...
save().done(function( retValue ) {
alert('success ' + retValue);
});
Learn more about jQuerys Ajax and Deferred objects here and here.
May be this is causing the issue, as i see you are having 2 document ready handlers one in another.
/***/
$(function () { // <--------------------I think this doesn't has to be here,
// so remove it and try if this solves
// the issue
$("#save").click(function () {
var success = save();
alert("success " + success);
});
}); // <-------------------------------and this one too
I know that this question has been asked million times so my apologies.
I looked at all other examples and I dont understand why the following code is not working now.
I get undefined alert box when I place it outside the CheckinMap function.
Why is it?
$(document).ready(function() {
var MapData;
$(function CheckinMap() {
$.ajax({
type: "GET",
url: "content/home/index.cs.asp?Process=ViewCheckinMap",
success: function (data) {
MapData = data;
},
error: function (data) {
$("#checkinmap").append(data);
}
});
});
alert(MapData);
});
MapData is undefined because the alert is executed while the ajax call is still running (ajax is asynchronous) and the response is not yet available. So change your code in this way
success: function (data) {
MapData = data;
alert(MapData);
},
or continue the code execution calling another function
success: function (data) {
continueExecution(data)
},
...
function continueExecution(data) {
alert(data)
}
or use deferred objects (on jQuery 1.5+)
$.ajax({
type: "GET",
url: "content/home/index.cs.asp?Process=ViewCheckinMap"
})
.done(function(data) { alert(data) })
The execution order is asynchronous. Currently the following steps are executed:
Ajax call
alert(MapData); // unknown
success function that sets MapData (or the error function that doesn't even set MapData)
You could alert in the success function (like suggested), but then you don't know if the variable is local to that function or is actually global. To test if MapData is actually global you can use setTimeout to alert the variable back.
Check out this modified example code:
// Global variable
var MapData;
// Global function
function test() {
alert(MapData);
}
$(document).ready(function() {
$.ajax({
type: "GET",
url: "http://www.google.nl",
success: function (data) {
MapData = data;
// Call global function with timeout
setTimeout(test, 10);
},
error: function (data) {
$("#checkinmap").append(data);
// Set error message instead of data (for testing)
MapData = 'error';
// Call global function with timeout
setTimeout(test, 10);
}
});
});
Or you can test it out here: http://jsfiddle.net/x9rXU/