ajax .done not running properly, seems to skip code - javascript

I have the code bellow running in a .each function. However when all is set and done and i do alert(inserted); or alert of any other variable they come out 0 which is what i have them set as when i declare them. I am sure they are within scope. I have a feeling this has to do with the timing of the ajax because if i put an alert after each call, the counter system works. Any thought? Also I am sure that the proper if statements are called as I attached alerts (as i said above which when i do this the counter works) and they fire properly AND no error codes as brought up from the consol.
$.ajax({
type: "POST",
url: "/php/filename.php",
data: {
one: $('#1').val(),
two: $('#2').val(),
three: $('#3').val(),
four: $('#4').val(),
five: $('#5').val(),
six: $('#6').val(),
seven: $('#classlist').val(),
id: $('#7').html()
}
}).done(function(msg) {
if (msg == "inserted") {
inserted++;
}
else if (msg == "updated") {
updated++;
}
else if (msg == "duplicate") {
duplicate++;
}
else if (msg == "0") {
fail++;
}
});

Ajax is asynchronous, your alert is happening before the ajax is complete. Store all of your ajax return values in an array, pass them all to .when, and then use it's done to alert the value of your variable.
var promiseArr = [], inserted = 0;
for (var i = 0; i < 30000; i++) { // <--- unrealistic number of iterations
promiseArr.push($.ajax({url:"foo.php?id=" + i}).done(function(){
inserted++;
}));
}
$.when.apply($,promiseArr).done(function(){
alert(inserted);
});

Related

jQuery $.when.apply not working as expected

I am trying to use jQuery's $.when.apply to wait for an unknown number of requests to finish before calling the next function (loadTab). I thought I was using it correctly, but it is definitely not waiting for all the requests to finish before loadTab() is called, so I'm not sure what is wrong. Here is my code:
function update(changes) {
var deferreds = [];
// must loop over changes and call individual requests
for(var i = 0; i < changes.length; i++) {
var direction = changes[i]['direction'];
var data = changes[i]['data'];
if(direction == 'add') {
deferreds.push[add(data)];
}
else if(direction == 'edit') {
deferreds.push[edit(data)];
}
else if(direction == 'delete') {
deferreds.push[delete(data)];
}
}
return $.when.apply($, deferreds); // this when is resolving too soon
}
function add(data) {
return $.ajax({
url: 'add',
data: data,
method: 'post',
error: ajaxErrorFcn
})
.then(function(response) {
handleTimeout(response);
});
}
function edit(data) {
return $.ajax({
url: 'edit',
data: data,
method: 'post',
error: ajaxErrorFcn
})
.then(function(response) {
handleTimeout(response);
});
}
function delete(data) {
return $.ajax({
url: 'delete',
data: data,
method: 'post',
error: ajaxErrorFcn
})
.then(function(response) {
handleTimeout(response);
});
}
// this is the sequence of events I'm trying to sort out
showLoad('#container');
var changes = buildChangesArray();
update(changes)
.then(function(response) {
if(handleTimeout(response)) {
// this is executing before the requests triggered by update() are complete
return loadTab()
.then(function(response) {
// do some other stuff
});
}
})
.then(function(response) {
hideLoad('#container');
});
Update:
The original issue was resolved (There was a typo in my calls to .push(), used brackets instead of parentheses), but now I have a new issue with this code. I need to modify the update() function to run the delete actions first, and then run the add and edit actions. This is what I have, but now I am seeing the add and edit actions start to run before the delete actions finish:
function update(changes) {
var deferreds = [];
var deletes = [];
// must loop over changes and call individual requests
for(var i = 0; i < changes.length; i++) {
var direction = changes[i]['direction'];
var data = changes[i]['data'];
if(direction == 'add') {
deferreds.push(add(data));
}
else if(direction == 'edit') {
deferreds.push(edit(data));
}
else if(direction == 'delete') {
deletes.push(delete(data));
}
}
// we need to perform all the delete operations first, then the adds/edits
return $.when.apply($, deletes) // this when is resolving too soon
.then(function(response) {
return $.when.apply($, deferreds);
});
}
Well, looks like I found my own solution :)
I think the problem was that when pushing the asynchronous calls to add(), edit(), and delete() into the arrays, they are also getting called at that point! This wasn't a problem in the first version because then it didn't matter what order the adds, edits, and deletes were performed in as long as they were all done before loadTab() was called. However, it does pose a problem if all the deletes need to be called before calling any adds or edits, because any adds or edits found in the array before the deletes will start running as soon as they are pushed to the array rather than waiting for the deletes.
To fix this, I changed the code to the following:
function update(changes) {
var loop = function(deleteOnly) {
var array = [];
// must loop over changes and call individual requests
for(var i = 0; i < changes.length; i++) {
var direction = changes[i]['direction'];
var data = changes[i]['data'];
if(direction == 'add' && !deleteOnly) {
array.push(add(data));
}
else if(direction == 'edit' && !deleteOnly) {
array.push(edit(data));
}
else if(direction == 'delete' && deleteOnly) {
array.push(delete(data));
}
}
return array;
};
// we need to perform all the delete operations first
return $.when.apply($, loop(true)) // true means only get the deletes
.then(function(response) {
return $.when.apply($, loop(false)); // false means only get the adds/edits
});
}
So the requests are still starting running as soon as they are pushed into the array, but this way we can separate the deletes to makes sure they finish first.

Parameters not passing into an ActionResult

So I have this Javascript code:
function AddToStuff(somethingID) {
var stuffID = document.getElementById('StuffID').innerText;
alert("First Alert: " + stuffID);
if (stuffID == -1) {
document.getElementById('StuffID').innerText = 0;
stuffID = 0;
}
alert("Second Alert: " + stuffID)
StuffCallback.fire(somethingID, stuffID);
}
$(document).ready(function () {
initializeStuff();
});
var StuffCallback = $.Callbacks();
function initializeStuff() {
StuffCallback.add(function (somethingID, stuffID) {
$.ajax({
url: "pos/Stuff",
type: "GET",
traditional: true,
data: {
somethingID: somethingID,
stuffID: stuffID
},
success: function (result, textStatus, xhr) {
alert("Third Alert: " + stuffID);
var contentArea = $("#Stuff");
contentArea.html(result);
$("#Stuff").hide();
$("#Stuff").show({ duration: 250 });
}
});
});
}
And this C# code:
public ActionResult Stuff(int somethingID = 0, int stuffID = -1)
{
if (stuffID == -1)
{
//do something
}
else if (stuffID == 0)
{
//do something else
}
else
{
//do something else
}
}
return View();
}
}
The problem is that the C# method always has stuffID at -1 and so performs the first "if" case.
When the page loads and $(document).ready calls initializeStuff, the stuffID is -1, so it performs the first "if" case, as desired. Later, when AddToStuff() is called, because the user clicked on something, it reads in my element StuffID, which I have initialized to -1. The First Alert does display the -1. Then StuffID gets changed to 0 in the "if" inside AddToStuff() and the Second Alert does display the 0.
But then I have the Javascript function AddToStuff call the C# function again, but the goal is to have the second parameter (stuffID) be the 0 instead of the -1. And even the Third Alert will display a 0. But in the C# function, the stuffID is still -1.
I can't pass in the 0. Any help would be appreciated.
Note: I generalized my code into "something" and "stuff", but that part should be working fine - the functions call when I expect them to.
Suggestions:
Use fiddler or developer tools in your browser to see what is being passed to the server (0, or -1)?
The default value to Stuff(int somethingID = 0, int stuffID = -1) can also be causing the problem if the data received by the server is not correct.
Set the data type in your Ajax call dataType: 'json'
If you are using a GET request, make sure that you have set the data object correctly
If I comment all your JS code and just write
StuffCallback.fire(0, 5);
it works nicely, as it accepts 5. That means that your stuffID is not being recognized as int. What kind of a HTML element is it? I see you are using .innerText. As you are using jQuery already, why not use
var stuffID = $('#StuffID').val();
I have noticed your problem though. You have a typo.
getElementById('StuffID') and your p is called "stuffID". JS IS case sensitive, so it will be enough to fix that, but please use
<input type="hidden" ID="StuffID" value="#stuffID">
as that's proper HTML markup

How to ensure that a function containing multiple $.ajax calls runs fully synchronously and also allows for browser repaints as it executes

I've been working on getting a function written to:
1) Process an input array using $.ajax calls to fill an output array (below this is inputList)
2) Below is what I have, but I'm having issues with it:
requestData(), when I call it, runs straight through to processing the outputList array without having fully populated/filled it - it puts one value into it then starts to process that, but the function still apparently runs on seperately to the subsequent processing asynchronously. I need it to be fully synchronous so that it does not return until the inputList array has been fully processed.
I'm not seeing the browser repainting the div that has its html updated on every call of the runajax() function - I'm attempting to do this with a setTimeout.
3) I've set the ajax request to be synchronous (async : false) - but this doesn't seem to help
I've tried to use jQuery's $.when to provide an ability to ensure that everything gets called in sequence - but clearly I'm not doing this correctly.
Would appreciate any help - I've asked previous related questions around this and had some useful help - but I've still not resolved this!
Thanks
//declare holding function requestData - expects a non-empty input data array named inputList
function requestData() {
//declare inner function runajax
function runajax() {
if(inputList.length > 0) {
//get first item from inputlist and shorten inputList
var data = $.trim(inputList.shift());
function getData() {
//send the data to server
return $.ajax({
url: 'sada_ajax_fetch_data.php',
cache: false,
async: false,
method: "post",
timeout: 2000,
data: {
requesttype: "getmydata",
email: encodeURIComponent(data)
}
});
}
function handleReturnedData (response) {
response = $.trim(decodeURIComponent(response));
//update the div inner html
if(response == "Failed") {
$('#fetchupdatestatus').html('There was an error retrieving the data you requested!');
} else {
$('#fetchupdatestatus').html('The item returned was '+response);
}
//add the response from ajax to the end of the outputList array
outputList.push(response);
//set up the next ajax call
var doNextBitOfWork = function () {
runajax();
};
//call setTimeout so that browser shows refreshed div html
setTimeout(doNextBitOfWork, 0);
//return
return $.when();
}
//do the next ajax request and response processing
return getData().done(handleReturnedData);
} else {
//did the last one so return
return $.when();
}
}
//kick off the ajax calls
runajax();
}
var inputList = new Array();
var outputList = new Array();
.....load +/- 100 values to be processed using ajax into array inputList
requestData();
.....process stuff in array outputList
.....etc
There was my answer with "you're doing it wrong" earlier, but then I just decided to show, how you can do it (almost) right: https://jsfiddle.net/h4ffz1by/
var request_maker = {
working: false,
queue: [],
output: [],
requestData: function(inputList) {
if (request_maker.working == true) {
return false;
}
request_maker.output = [];
request_maker.working = true;
while (inputList.length > 0) {
var data = $.trim(inputList.shift());
request_maker.queue.push(data);
}
console.log(request_maker.queue);
request_maker.doTheJob();
return true;
},
doTheJob: function() {
current_data_to_send = request_maker.queue.shift();
console.log(current_data_to_send);
if (typeof current_data_to_send != 'undefined' && request_maker.queue.length >= 0) {
$.ajax({
url: '/echo/json/',
cache: false,
method: "post",
timeout: 2000,
data: {
requesttype: "getmydata",
email: encodeURIComponent(current_data_to_send)
},
success: function(data, status, xhrobject) {
console.log(xhrobject);
request_maker.handleReturnedData(data);
},
});
} else {
request_maker.working = false;
console.log('all data has been sent');
}
},
handleReturnedData: function(response) {
console.log(response);
response = $.trim(decodeURIComponent(response));
//response= 'Failed';//uncomment to emulate this kind of response
if (response == "Failed") {
$('#fetchupdatestatus').append('There was an error retrieving the data you requested!<br/>');
} else {
$('#fetchupdatestatus').append('The item returned was ' + response + '<br/>');
request_maker.output.push(response);
}
request_maker.doTheJob();
if (request_maker.working == false) {
console.log('all requests have been completed');
console.log(request_maker.output);
}
}
}
inputList = [1, 2, 3, 4, 5, 6];
if (request_maker.requestData(inputList)) {
console.log('started working');
}
if (!request_maker.requestData(inputList)) {
console.log('work in progress, try again later');
}
Note that I've changed request path to jsfiddle's ajax simulation link and replaced html() with append() calls to print text in div. The calls are made and get handled in the same order as it is in inputList, still they don't lock user's browser. request_maker.output's elements order is also the same as in inputList.
Have in mind, that you will need to add error handling too (probably just a function that pushes 'error' string into output instead of result), otherwise any ajax error (403/404/502, etc.) will get it "stuck" in working state. Or you can use complete instead of success and check request status right there.
UPD: Answer to the question: you cannot get both. You either use callbacks and let browser repaint inbetween asynchroneous requests or you make requests synchroneous and block browser untill your code finished working.
UPD2: There actually is some information on forcing redraw, however I don't know if it will work for you: Force DOM redraw/refresh on Chrome/Mac

Jquery - Keeping datas after post request

I'm trying to loop a jquery post request and stopping it when the result data is correct.
while(flag == 0){
$.post("file.php", {
first:first_p,
second:second_p
}, function(data){
if(data == 1){
flag = 1;
}
}
}
But the problem seems to be that flag is only available within the post request and not the while loop.
How can I solve this problem?
Thanks.
It's probably because ajax takes time to query the server, and during that time it will continue to send requests, while it's in the while loop.
It would be a better design to to wrap your code in a function, and if an incorrect response has been provided, ie data is not 1, then call the function again.
function checkFile(){
$.post("file.php", {
first:first_p,
second:second_p
}, function(data){
if(data == 1){
flag = 1;
} else {
checkFile();
}
}
}

jQuery $.post() in recursive way (loop)

I learn jQuery and don't understand this situation:
When running this code in debug mode all work well. But when running this code normal, calback function don't starts. Why?
In non debug mode I have -> "Start" -> "End 10"
Browser: Google Chrome.
var nrPost = 10;
$("#buttnX").click(function() {
alert("Start");
GoPosts();
End();
});
function End() {
alert('End ' + nrPost);
};
function GoPosts() {
$.ajaxSetup({async:false});
var url = "http://......";
var data = { ... };
$.post(url, data, Callback, 'json');
};
function Callback(response) {
if (response.error) {
return;
}
nrPost--;
if (nrPost > 0) [
GoPosts();
} else {
return;
}
};
You had an extra }; in your code. I changed it around a bit to use jQuery and put it up on jsfiddle.
http://jsfiddle.net/rH8RV/19/
It should alert: "Start" and then "End 10", that's correct based on how you wrote your code. Were you expecting anything else?
I don't know what you're planning to do with your recursive implementation, but if that's all, you could actually do this:
function startLoop(nrPost) {
// No need to put this in a loop
$.ajaxSetup({ async: false });
for (var i = 0; i < nrPost; i++) {
alert('Start ' + i);
var url = 'http://......';
var data = {};
$.post(url, data, function (response) {
if (response.error)
return;
alert('End ' + i);
}, 'json');
}
}
$('#buttnX').click(function () { startLoop(10) });
Hope that helps!
I imagine you are expecting the display to be:
"Start"
"End 0"
This is unlikely to work with your solution.
Your Ajax call $.post(url, data, Callback, 'json'); is asynchronous. This means that once the $.post method returns, the request is sent to the URL you have provided. However, Callback is not called until JQuery receives the answers. What happens immediately is that GoPosts terminates and the program continues. It comes back to line 5 of your code, inside the anonymous function in your click handler. At that point, End() is called and alerts "End 10".
You probably want to put your call to End in Callback instead:
function Callback(response)
{
if (response.error)
{
return;
}
nrPost--;
if(nrPost>0)
GoPosts();
else
{
End(); // You only want to end once you have made you nrPost calls to GoPost
return;
}
};

Categories