I'm fairly new to AJAX, but working on a project that requires an ajax call to validate a specific value, then make another ajax call if the first returns the expected value. I am trying to implement the .done/.fail model, but can't find a way to prevent both calls from happening simultaneously, rather than once the first call is done and successful.
The following code will call the ajaxCall function twice, but concurrently rather than consecutively. I have researched a ton of code, including Nested AJAX Calls, jQuery getting rid of nested ajax functions, and $.when.done callback isn't working, but none seem to fit my exact scenario, or maybe I just don't understand the code. Either way, I haven't been able to find a solution, and any help will be much appreciated!
var xReturn = ajaxCall("1");
xReturn.done(function(msg){
console.log("callback "+msg+" successful");
// if successful, place second call
if(parseInt(msg)==1)
xReturn = ajaxCall("2");
});
function ajaxCall(mop){
return $.ajax({
url: "getItem.php",
type: "POST",
data: {code: '<?php echo $code; ?> ', op:mop}
});
}
It seems like promises may be the way to go, but I can't wrap my head around how to use them in this scenario. Thanks in advance for any pointers in the right direction.
Update:
I ran through a battery of tests with different results. For my final test last night, I placed another console.log(msg); directly after ajaxCall("2"); Each time the resulting msg was always "1", leading me to believe the calls were not happening properly. This result tells me that xReturn.done(function(msg)... is only being called once, but I thought it would be called with each ajax call.
With this new information, I will perform additional testing tonight and report back.
Thanks
You need to bind a .done() method to each promise. xReturn.done() binds a function to that promise.
When you do xReturn = ajaxCall("2");, you are replacing xReturn with a different object. This object does not have a .done() method bound to it.
You need to bind .done() to each promise, that doesn't happen automatically.
var xReturn = ajaxCall("1");
// This binds the callback to this *specific* promise
xReturn.done(ajaxDone);
function ajaxCall(mop){
return $.ajax({
url: "getItem.php",
type: "POST",
data: {code: '<?php echo $code; ?> ', op:mop}
});
}
function ajaxDone(msg){
console.log("callback "+msg+" successful");
// if successful, place second call
if(parseInt(msg)==1){
xReturn = ajaxCall("2");
// Bind a callback to this *new* object
xReturn.done(ajaxDone);
}
}
There are multiple ways to go about this problem.
You could simply call the second ajax call from the success of the first. Something on the following lines
function ajaxCall(mop){
$.ajax({
url: "getItem.php",
type: "POST",
data: {code: '<?php echo $code; ?> ', op:mop}
}).done(function(msg) {
console.log("First call is done. Received data as ", msg);
if(parseInt(msg)==1) {
$.ajax({
//Repeat Options
}).done(function(newMsg)) {
console.log("We're done");
};
}
});
}
}
If you do want to use the .done/.fail model, you could try using $.when.
Here is a working fiddle that does consecutive calls using the same function.
function ajaxCall(mop){
return $.ajax({
url: "/echo/json/",
type: "POST",
data: {
json: $.toJSON({data: mop}),
delay: Math.floor(Math.random()*4)
}
});
}
$.when(ajaxCall("1")).done(function(data) {
console.log("Done with first call. About to call second");
if(data /* and add whatever conditions you need to call the next function */) {
ajaxCall("2");
}
});
Try it like this.
ajaxCall("1");
function ajaxCall(mop){
$.post( "getItem.php", {code: '<?php echo $code; ?> ', op:mop})
.done(function( msg) {
console.log("callback "+msg+" successful");
if(parseInt(msg)==1)
ajaxCall("2");
});
}
And also you can use these with previous code
.fail(function() {
alert( "error" );
})
.always(function() {
alert( "finished" );
How about that:
var xReturn1 = ajaxCall("1"),
xReturn2 = ajaxCall("2");
$.when(xReturn1, xReturn2).done(function( a1, a2 ) {
var data1 = a1[0], data2 = a2[0];
console.log("Both done");
});
Related
I am having an each loop in JQuery in which I trigger an ajax request. I want to display one success message after all the ajax calls are done executing.
My code goes below,
$('.container input:checked').each(function() {
json_data = $(this).parent().parent().find('.case_json').html()
$.ajax({
type: "POST",
url: "/some_action",
data: { json_data : json_data },
success: function(data) {
console.log('saved')
}
})
}).promise().done( function(){ $('#import_message').show().addClass('alert alert-success').html('Data imported successfully!') } );
But the problem with the code is my success message is getting displayed way before the ajax calls are done executing.
What am I doing wrong? Or do I need to change the way I implemented?
You need to combine usage $.map function with $.when, here is how it should look like:
$.when.apply($, $('.container input:checked').map(function() {
json_data = $(this).parent().parent().find('.case_json').html()
return $.ajax({
type: "POST",
url: "/some_action",
data: { json_data : json_data },
success: function(data) {
console.log('saved')
}
})
}))
.done( function(){
$('#import_message').show().addClass('alert alert-success').html('Data imported successfully!')
} );
Map function would create an array of deffereds, this array is passsed to $.when function, the problem is that it's doesn't support array as argument, but support any number of parameters, so we can use .apply() which take array of promises and pass to function as arguments.
I think you want to build an array of promises then use $.when.apply. There is a similar problem here: What is cleanest way to turn Array of JQuery Promises into a JQuery Promise of an Array?.
Apologies if this is a duplicate question, I've followed some steps from another question which didn't seem to help me. I am trying to retrieve some JSON data, store part of the data into a variable and use that variable in a separate function outside of the AJAX request.
My expected response from the json data is http://localhost:8000/tutorials/retrieve/?page=2 (This response shows if I log the variable inside of the AJAX code) however the actual response I get when I try to log the variable from another function is as follows:
n.Event {originalEvent: MouseEvent, type: "click", timeStamp: 1436727171161, jQuery21304066238570958376: true, toElement: div#loadmore.recentTutorials…}
Here is the current code
var recentFirstPage = '';
function retrieveTutorials(){
$.ajax({
type: "GET",
url: "/tutorials/retrieve",
dataType: "json",
success: function(data){
**some unrelated parsing code here**
//Set the variable to what I need
recentFirstPage = data.next_page_url;
},
error: function() {
alert("An error occurred processing AJAX request.");
}
});
}
$('#main-content-wrap').on('click', '.recentTutorials', function(recentFirstPage){
//Does not return expected result
console.log(recentFirstPage);
});
When I click .recentTutorials I expect the console to log the data from JSON however it doesn't. Can someone help clear up my error(s)?
The reason that it doesn't log the data from JSON s that the call is asynchronous. This means that the function will execute top to bottom without waiting for the call to finish.
One method that's used is to leverage deferred objects which return a promise on completion. You can accept an anonymous function to the invoker function so that it's call back is executed within the scope of the click.
Observe:
function retrieveTutorials(){
return $.ajax({
type: "GET",
url: "/tutorials/retrieve",
dataType: "json"
});
}
$('#main-content-wrap').on('click', '.recentTutorials', function(){
//store our function call as an ajax promise
var promise = retrieveTutorials();
//wait for the ajax to return so we can mutate the data
promise.done(function(data){
//now our data will be properly
recentFirstPage = data.next_page_url;
});
});
It seems to me that you are trying to log the data before the ajax is completed. It`s better to use deferreds . Try this:
function retrieveTutorials(){
return $.ajax({ // will return deferred object
type: "GET",
url: "/tutorials/retrieve",
dataType: "json",
success: function(data){
**some unrelated parsing code here**
//Set the variable to what I need
recentFirstPage = data.next_page_url;
},
error: function() {
alert("An error occurred processing AJAX request.");
}
});
}
$.when( retrieveTutorials() ).done(function ( data ) {
console.log(recentFirstPage);
});
The parameter in your click handler is the last and final nail in your coffin. It's always the jquery event and you shouldn't handle it at all.
You do need to call the retrieveTutorials() function within the handler and you need to pass it a callback function that will be executed on success. So your retrieveTutorials() function will look something like this:
function retrieveTutorials(success){
$.ajax({ type: "GET", url: "/tutorials/retrieve",
dataType: "json",
success: success,
error: function() { alert("An error occurred processing AJAX request.");
} }); }
And your click handler:
$('#main-content-wrap').on('click', '.recentTutorials', function(){
retrieveTutorials(function(data){
console.log(data.next_page_url);
});
});
You can also use all the promise based goodness in the other anwers, but the above would be an idiom you'll see again and again.
Method for handling ajax
function ajaxMethod(){
return $.ajax({
url: ajaxUrl,
type: "POST",
dataType: "JSONP",
jsonpCallback: ajaxCallback
});
}
Calls to this method:
dD(ajaxMethod());
aA(ajaxMethod());
bB(ajaxMethod());
cC(ajaxMethod());
aa,bb,cc,dd method have
promise.success(function (response) {
console.log(response);
});
Now aA response is coming in bB function,,bb response in cc function and as simultaneous call is coming.
Also tried using async true nothing happens.
Any suggestions?
With jsonpCallback you are telling jQuery to use a specific function name for the callback function (instead of generating a unique one), so each time you call it, you overwrite the previous function.
Just remove jsonpCallback: ajaxCallback.
While you are at it, remove type: "POST",, it is incompatible with JSONP.
I think this is what you are after.
This code is using the returned promise to wait for the result then passing the result to your other function.
ajaxMethod().success(function(response)
{
dD(response);
});
ajaxMethod().success(function(response)
{
aA(response);
});
ajaxMethod().success(function(response)
{
cC(response);
});
ajaxMethod().success(function(response)
{
dD(response);
});
Your aA, bB, cC and dD methods can now be:
function <insertname>(response)
{
console.log(response);
}
If you want your request to come in the synchronous way, then try the following :
var callBack = $.Callbacks();
callBack.add(dD(ajaxMethod()));
callBack.add(aA(ajaxMethod()));
callBack.add(bB(ajaxMethod()));
callBack.add(cC(ajaxMethod()));
callBack.fire();
the above line of code will make sure the respective ajax call would get call.
I want to do something like this:
$.ajax({
url: SOMEWHERE,
...
success: function(data){
// do sth...
var new_url = data.url;
$.ajax({
url: new_url,
success: function(data){
var another_url = data.url;
// ajax call rely on the result of previous one
$.ajax({
// do sth
})
}
})
},
fail: function(){
// do sth...
// ajax call too
$.ajax({
// config
})
}
})
the code looks awful for me.
I wonder how to make it looks pretty. Some best practice?
I would consider breaking it up, maybe something like this.
function initialSuccessHandler(data) {
$.ajax({url:data.url, success:secondarySuccessHandler});
}
function secondarySuccessHandler(data) {
//do stuff
}
function initialFailHandler() {
$.ajax({...});
}
$.ajax({url:"whatever.com", success:initialSuccessHandler, fail: initialFailHandler});
There's not a whole lot you can probably do about it other than if the success function are similar (just need different URL's to new AJAX calls for example) you might be able to define a common function to call recursively, like this:
function do_something(data) {
// some logic
$.ajax({
url: data.url,
success: do_something(data);
fail: function (){
// handle failure
}
});
}
Use $.post instead of $.ajax that's lot easier and clean
$.post('yourAjaxUrl.com/index.html',(newVal:'val'), function(data) {
$.post('yourSecondAjaxUrl.com/index.html',{newVal1:data}, function(data) {
//do something
});
});
Or if you want to use GET request use like this.
$.get('yourAjaxUrl.com/index.html',(newVal:'val'), function(data) {
$.get('yourSecondAjaxUrl.com/index.html',{newVal1:data}, function(data) {
//do something
});
});
Other answers are mostly fine too as using functions in a lot of case will definitely help your code. The problem of your function is that it's doing to much things all in once. Decreasing the complexity of the function will help a LOT (separating different action in different functions).
There's some good training videos of Bocoup here which can help you decrease a function complexity: http://training.bocoup.com/screencasts/
Although, a basic answer to the callback inferno:
You could use jquery Deffered who do a good job in certain case by preventing the "indentation pyramid of doom". (But won't decrease the complexity of your function)
$.ajax({
url: SOMEWHERE
})
.pipe(function() {
// First success callback
return $.ajax({
url: new_url
});
}, function() {
// First error callback
$.ajax({
// config
});
// we ain't returning anything so this end here.
})
.done(function( data ) {
// Second success callback
var another_url = data.url;
// ajax call rely on the result of previous one
$.ajax({
// do sth
})
});
Deferred can fit in a whole lot more of context, and learning about them is really useful. That's only the basic idea behind them.
Hope this help!
I'm no professional in JavaScript and I've seen searching a long time for this on the internet.
I'm having a problem getting a variable from another function. My code is looking like this:
var variabeltje;
$.post('js/ajax/handle_time.php', {'time': $(this).find('input').val()},
function(data) {
alert(data);
variabeltje=data;
}
);
alert(window.variabeltje);
The variable variabeltje must get the information from data. When I put the alert below variabeltje=data it's working, but I need the data variable after the function.
Edit:
I have changed it to what people said, I now have this:
var XHR = $.post('js/ajax/handle_time.php', {time: $(this).find('input').val()});
XHR.done(function(data) {
console.log(data);
getData(data);
});
function getData(data) {
if(data == 'true') {
alert(data);
$(this).unbind('keypress');
$(this).html($(this).find('input').val());
}
}
But now the $(this) isn't passing into the function. How can I solve this?
As it's asynchronous the data will only be available after the ajax call is completed, so you'll have to modify your code to work with that and wait until after the ajax is done to do your stuff:
var XHR = $.post('js/ajax/handle_time.php', {time: $(this).find('input').val()});
XHR.done(function(data) {
console.log(data);
});
or in other function:
function XHR(time){
return $.post('js/ajax/handle_time.php', {time: time});
}
function doStuff(element) {
XHR($(element).find('input').val()).done(function(data) {
console.log(data);
});
}
EDIT:
based on your edit, it looks like it's a scoping issue:
var self = this,
XHR = $.post('js/ajax/handle_time.php', {time: $(self).find('input').val()});
XHR.done(function(data) {
console.log(data);
getData(data);
});
function getData(data) {
if(data == 'true') { //normally you'd try to pass true as a boolean, not string
alert(data);
$(self).unbind('keypress').html($(self).find('input').val());
}
}
This is because the $.post method runs asynchronously.
Please take a look at this question on SO.
Edit: You can change your code and put the part after post method inside the body of the anonymous function triggered on success. As follows:
$.post('js/ajax/handle_time.php', {'time': $(this).find('input').val()},
function(data) {
alert(data);
variabeltje=data;
alert(window.variabeltje); // or whatever function you need to call, call it here...
}
);
The problem is that post method is executed asynchronously. This is, a post is sent to the server but execution flow continues so your alert is trying to access a value that wasn't set since post haven't returned yet.
You could use ajax method if you need a synchronic execution.