Wait for chain of AJAX calls to complete [duplicate] - javascript

This question already has answers here:
jQuery callback for multiple ajax calls
(14 answers)
Closed 7 years ago.
I have a function that calls other functions which have AJAX requests. I want all of the AJAX calls to complete before calling the last function. I initialize an array and need all of the AJAX calls to have successfully completed before going to the last step. When I run the code in the browser the console is showing the last method being called before the first AJAX call is completed. I've tried using .done however I'm still getting the same result.
function Search() {
var userIds = [];
var certName = $('#certificationName').val();
var skillNumberOne = $('#skillName1').val();
var skillNumberTwo = $('#skillName2').val();
var skillNumberThree = $('#skillName3').val();
var locale = $('#location').val();
CertMatchResults(certName, userIds);
SkillMatchResults(skillNumberOne, userIds);
SkillMatchResults(skillNumberTwo, userIds);
SkillMatchResults(skillNumberThree, userIds);
LocationMatchResults(locale, userIds);
console.log(userIds);
DisplayMatches(userIds);
}
function CertMatchResults(certName, userIds) {
if (certName == '') return;
$.getJSON('json_data.php', { method: 'matchCerts', certName: certName }, function(data) {
$.each(data, function(key, value) {
console.log("Cert Match >>> " + value.user_id);
userIds.push(value.user_id);
});
}).done(function() {
console.log("Cert match completed.");
});
}
function SkillMatchResults(skillName, userIds) {
if (skillName == '') return;
$.getJSON('json_data.php', { method: 'matchSkill', skillName: skillName }, function(data) {
$.each(data, function(key, value) {
console.log("Skill Match >>> " + value.user_id);
userIds.push(value.user_id);
});
});
}
... other calls below ... I can add them if needed
DisplayMatches needs all of the AJAX calls in each of the functions before it to complete.

You should create a chain of deferred objects.
Each ajax call returns Deferred object and then you can gather them in jQuery.when function and invoke the last function once all the ajax calls resolved their promises in jQuery.then function.
$.when( $.getJson("url1"), $.getJson("url2") ).then(function( valueFromUrl1, valueFromUrl2 ) {
functionThatShouldBeInvokedLast();
});

Have all functions call the DisplayMatches when they are done and check the userIDs array if it is complete ... and when it is you know all is done so you just run it, if not, exit

Related

jQuery Break loop in AJAX Request with Callback

I am a bit stuck with my script: I am trying to live search through my pages via jQuery and AJAX. In this example I want to search in 2 sites for a keyword and I want to break the AJAX-loop when I have found something:
var urls = [
'http://docs.db-dzine.com/woocommerce-advanced-categories/',
'http://docs.db-dzine.com/woocommerce-catalog-mode/',
];
var found = false;
$.each(urls, function(i, url) {
ajaxSearchPage(url, keyword, function(found) {
console.log(found);
if(found) {
return true;
}
});
if(found) {
return true;
}
});
I do not know how to break the loop here ... please help.
And my AJAX Function:
var ajaxSearchPage = function(url, keyword, callback) {
$.ajax({
url: url,
type: "GET",
dataType: "html",
success: function(response) {
// Do not load images
var page = response.replace(/<img/gi, '<noload');
// Create parsable body for jQuery
var body = $('<div id="body-mock">' + page.replace(/^[\s\S]*<body.*?>|<\/body>[\s\S]*$/ig, '') + '</div>');
// Find the keyword in content
var content = body.find('#content:containsNC("'+ keyword +'")');
if(content.length > 0) {
searchResultsWrapper.show();
callback(true);
} else {
callback(false);
}
}
});
};
jQuery documentation states:
We can break the $.each() loop at a particular iteration by making the
callback function return false. Returning non-false is the same as a
continue statement in a for loop; it will skip immediately to the next
iteration.
AJAX calls are a deferred operation so your whole loop is likely to run. This is because you cannot know when all the network requests will complete. If you look at jQuery.ajax you will notice that it returns a jqXHR object. jqXHR has an abort method to cancel the network request.
So what we actually want to do is cancel all the requests once we have the desired result. To do that we will have to queue up the requests and then cancel.
var ajaxQueue = [];
$.each(urls, function(i, url) {
ajaxQueue.push(ajaxSearchPage(url, keyword, function(found) {
if(found) {
ajaxQueue.forEach(jqXHR=>jqXHR.abort());
}
}));
});

My function doesn't return the right value

I'm having trouble with the following function:
function getLengthData(date, driverId) {
var length = [];
$
.get('http://xx/x/x/' + date + '/' + driverId + '')
.done(function (data) {
var test = data.length;
length.push(test);
});
return length;
}
This returns nothing while it should return an array with 1 element, the length of the data array. The next function uses the same way and works perfectly:
function getStopsFromStorage() {
var stops = [];
_2XLMobileApp.db.stopsData.load().done(function (result) {
$.each(result, function () {
stops.push(this.Id);
});
})
return stops;
}
I kinda have an idea what the problem is but no idea how to fix.
Any help would be greatly appreciated!
Thx
As you've already learned, you won't be able to use return with asynchronous functions like $.get(). The return statement that follows $.get() will occur before the request has actually completed.
One option you can do is to adjust the function to accept its own callback. Another is to return a Deferred/Promise so the calling code can apply the callback itself.
Better still may be to support both like $.get() and other Ajax methods.
function getLengthData(date, driverId, callback) {
return $.get('http://xx/x/x/' + date + '/' + driverId + '')
.then(function (data) {
return data.length;
})
.done(callback);
}
getLengthData('2013-07-31', '1234', function (length) {
// use `length` here
});
getLengthData('2013-07-31', '1234').done(function (length) {
// use `length` here
});
This snippet does require jQuery 1.8+ as it makes use of chaining deferreds with .then(). In short, the return data.length within getLengthData just changes the arguments for any further .done() callbacks, such as callback.
Example: http://jsfiddle.net/jMrVP/
Are you sure your host and port match in http://194.78.58.118:9001?
This can be Same origin policy problem.
EDIT:
AJAX is asynchronous JavaScript and XML. So you returning value before you get response from server.

AJAX not working without alert box [duplicate]

This question already has answers here:
How do I return the response from an asynchronous call?
(41 answers)
Closed 9 years ago.
I'm trying to use this code:
$('#form1').on('submit', function(){
var txtItemCode = $('#txtItemCode').val();
$.post("userExist.php", {
txtItemCode: txtItemCode,
}, function(data,status){
//alert("Data: " + data + "\nStatus: " + status);
$('#status').html(data);
reply = data;
//alert(reply);
});
alert('');
if (reply=='OK'){
return false;
}
});
I need to check if data=="OK" and return false, but when I remove the alert, it no longer works. Why is this, and how can I make it work?
The reason it works when you introduce the alert is because it stops execution and gives enough time for the asynchronous call to finish.
You're not getting the right value because by the time the post request is finished and the callback is executed your javascript has already finished executing.
You have a few options here:
Declare a global variable and perform a synchronous call, you can either do it with the code ABC posted or call $.ajaxSetup({ async: false }) before your POST call. Assign the return value to the global variable and validate against that.
use jQuery's ajaxStop: $(document).ajaxStop(function() { //check your values here, still need to declare a global });
Write the value to a hidden div/as an attribute anywhere in the DOM and have a timer that periodically checks it until there's a value.
in your code the ajax call is working asynchronous, so no matter you are responded or not your next lines will be executed. In sync call your next lines will not be executed untill you get response.
you could turn into $.ajax from $.post and make async property false. Then you will be able to get and then you can get the value of "reply" after the ajax call is responded.
$.ajax({
type: 'POST',
url: url,
data: data,
success: success,
dataType: dataType,
async:false
});
A new thought came to my mind, please check this.
/**
* form submit
*/
$('#form1').on('submit', function() {
if (checkExistence()) {
return false;
}
});
/**
* check existence in db
* #returns {Boolean}
*/
function checkExistence() {
var txtItemCode = $('#txtItemCode').val();
var result = true;
$.post("http://localhost/test.php", {
txtItemCode: txtItemCode,
}, function(data, status) {
//alert("Data: " + data + "\nStatus: " + status);
if (data == 'OK') {
$('#status').html(data);
result = true;
} else {
$('#txtItemCode').val('').focus();
$('#status').html("Code is already existing, please provide another one:)")
result = false;
}
});
return result;
}
Note: You can swap the value of result and check.

Any better way to combine multiple callbacks?

I need to call an async function (with loop) for multiple values and wait for those results. Right now I'm using the following code:
(function(){
var when_done = function(r){ alert("Completed. Sum of lengths is: [" + r + "]"); }; // call when ready
var datain = ['google','facebook','youtube','twitter']; // the data to be parsed
var response = {pending:0, fordone:false, data:0}; // control object, "data" holds summed response lengths
response.cb = function(){
// if there are pending requests, or the loop isn't ready yet do nothing
if(response.pending||!response.fordone) return;
// otherwise alert.
return when_done.call(null,response.data);
}
for(var i=0; i<datain; i++)(function(i){
response.pending++; // increment pending requests count
$.ajax({url:'http://www.'+datain[i]+'.com', complete:function(r){
response.data+= (r.responseText.length);
response.pending--; // decrement pending requests count
response.cb(); // call the callback
}});
}(i));
response.fordone = true; // mark the loop as done
response.cb(); // call the callback
}());
This isn't all very elegant but it does the job.
Is there any better way to do it? Perhaps a wrapper?
Async JS to the rescue (for both client-side and server-side JavaScript)! Your code may look like this (after including async.js):
var datain = ['google','facebook','youtube','twitter'];
var calls = [];
$.each(datain, function(i, el) {
calls.push( function(callback) {
$.ajax({
url : 'http://www.' + el +'.com',
error : function(e) {
callback(e);
},
success : function(r){
callback(null, r);
}
});
});
});
async.parallel(calls, function(err, result) {
/* This function will be called when all calls finish the job! */
/* err holds possible errors, while result is an array of all results */
});
By the way: async has some other really helpful functions.
By the way 2: note the use of $.each.
You can use the jQuery Deferred object for this purpose.
var def = $.when.apply(null, xhrs) with xhrs being an array containing the return values of your $.ajax() requests. Then you can register a callback def.done(function() { ... }); and use the arguments array-like object to access the responses of the various requests. to properly process them, remove your complete callback and add dataType: 'text' and use the following callback for done():
function() {
var response = Array.prototype.join.call(arguments, '');
// do something with response
}

Function inside jquery returns undefined [duplicate]

This question already has answers here:
How do I return the response from an asynchronous call?
(41 answers)
Closed 4 years ago.
The function I called inside jquery returns undefined. I checked the function and it returns correct data when I firebugged it.
function addToPlaylist(component_type,add_to_pl_value,pl_list_no)
{
add_to_pl_value_split = add_to_pl_value.split(":");
$.ajax({
type: "POST",
url: "ds/index.php/playlist/check_folder",
data: "component_type="+component_type+"&value="+add_to_pl_value_split[1],
success: function(msg)
{
if(msg == 'not_folder')
{
if(component_type == 'video')
{
rendered_item = render_list_item_video(add_to_pl_value_split[0],add_to_pl_value_split[1],pl_list_no)
}
else if(component_type == 'image')
{
rendered_item = render_list_item_image(add_to_pl_value_split[0],add_to_pl_value_split[1],pl_list_no)
}
}
else
{
//List files from folder
folder_name = add_to_pl_value_split[1].replace(' ','-');
var x = msg; // json
eval('var file='+x);
var rendered_item;
for ( var i in file )
{
//console.log(file[i]);
if(component_type == 'video')
{
rendered_item = render_list_item_video(folder_name+'-'+i,file[i],pl_list_no) + rendered_item;
}
if(component_type == 'image')
{
rendered_item = render_list_item_image(folder_name+'-'+i,file[i],pl_list_no) + rendered_item;
}
}
}
$("#files").html(filebrowser_list); //Reload Playlist
console.log(rendered_item);
return rendered_item;
},
error: function()
{
alert("An error occured while updating. Try again in a while");
}
})
}
$('document').ready(function()
{
addToPlaylist($('#component_type').val(),ui_item,0); //This one returns undefined
});
The function addToPlaylist doesn't return anything. It makes an asynchronous request, which eventually executes a callback function, which returns something. The original addToPlaylist function is long done and returned by the time this happens though, and the callback function returns to nobody.
I.e. the success: function(msg) { } code executes in a different context and at a later time than the surrounding addToPlaylist function.
Try this to see it in action:
function addToPlaylist() {
$.ajax({
...
success : function () {
alert('second'); // comes after 'first'
return null; // returns to nobody in particular
}
});
alert('first'); // comes before 'second'
return 'something'; // can only return here to caller
}
You're making your request via AJAX, which by definition is asynchronous. That means you're returning from the function before the AJAX request completes. In fact, your return statement is meaningless as it returns from the callback function, not your addToPlaylist function.
You have a couple of choices. The first one is better.
First, you can work with the asynchronous nature of the AJAX request and pass a callback into your addToPlaylist method (much like you're passing in the anonymous callback to the ajax function) and have the AJAX callback, call that function instead of doing the return. That way your request completes asynchronously and doesn't lock up your browser while it's going on.
function addToPlaylist(component_type, add_to_pl_value, pl_list_no, cb )
{
...yada yada yada...
$.ajax({
...
success: function(data) {
...
if (cb) {
cb.apply(this, rendered_item );
}
}
});
}
Second, you can add the option aSync: false to the ajax call. This will force the AJAX call to run synchronously (essentially it just loops until the call returns then calls your callback). If you do that, you need to capture a local variable in your addToPlaylist function inside the callback and assign the value(s) to it from the callback. At the end of the addToPlaylist function, return this variable as the result.
function addToPlaylist(component_type, add_to_pl_value, pl_list_no )
{
...yada yada yada...
var result = null;
$.ajax({
aSync: false,
...
success: function(data) {
...
result = rendered_item;
}
});
return rendered_item;
}
I agree with deceze. What you need to do is perform the necessary action(s) for rendered_item in the success function rather than relying on getting something back from addToPlayList().

Categories