Can not change variable [duplicate] - javascript

This question already has answers here:
How do I return the response from an asynchronous call?
(41 answers)
Closed 9 years ago.
I have some JavaScript code (below).
In every case, at the end of the loop the $sid should be zero, but "alert($sid)" always give me 200. I can't find an error in my code. Could you please help me?
Thanks in advance.
$sid = 200;
$canAppend = 0;
$iPage = 1;
while ($sid && $canAppend==0 && $iPage==1) {
alert($sid);
$.ajax({
url: "tmp/result1.html",
success: function(html)
{
if(html)
{
$("#inzeraty").append(html);
$('div#loadmoreajaxloader').hide();
$sid = 0;
}
},
error: function()
{
$sid = 0;
}
});
}

The issue is that the ajax call is asynchronous. JavaScript puts this makes the request to the server and then continues on to the next line of code. Your success and error callback functions are not being called until after JavaScript has completed execution of your while loop.
Furthermore, the structure of the code doesn't make any sense at all (sorry, not trying to be rude). The $canAppend and $iPage variables are not used or changed. What this code will do is enter into a loop and never exit. Why is this? It's because the call to $.ajax() is non blocking. It won't wait there until the request is completed, it will continue on. Since JavaScript is (essentially) single threaded the callbacks for error and success CAN'T be executed until the current execution process is completed. Because the success and error handlers can't be run the $sid can't be set. Because $sid can't be sent the code can't exit the while loop.
I don't see how you code is actually making use of the while loop. Instead, just call the $.ajax() function and handle the results in your success handler. Try this on for size to help you better understand what's going on:
$sid = 200;
alert("$sid is 200: " + $sid); // you'll see this first
$.ajax({
url: "tmp/result1.html",
success: function(html)
{
if(html)
{
$("#inzeraty").append(html);
$('div#loadmoreajaxloader').hide();
$sid = 0;
alert("$sid is now 0: " + $sid); // you'll see this third if html is not false
} else {
alert("$sid is STILL 200: " + $sid); // you'll see this third if html is false
}
},
error: function()
{
$sid = 0;
alert("you got an error, but $sid is now 0: " + $sid); // you'll see this third if there's an error
}
});
alert("$sid is still 200: " + $sid); // you'll see this second

By default ajax calls are asynchronous and the success or error functions are postponed until after $.ajax has retrieved tmp/result1.html. In your case, they'll be postponed forever because the while loop will keep the hand, $sid will remain equal to 200 and you'll keep piling up ajax calls.
A quick fix is to make your ajax call synchronous:
$.ajax({
url: "tmp/result1.html",
async: false,
success: function(html)
// etc.
Rewriting your code to avoid a mix of while loop and ajax might be a better idea (but I don't know your specific context).

Related

Failing to delay function until after 'for' loop has fully completed

I've got a tool in place which is splitting a large query into manageable chunks, then using a simple AJAX method to spit this out. The destination for the AJAX form is just a script which delegates some form data to a function, including which 'chunk' to process.
<script>
var passes = Math.ceil($max / $offset);
for (i = 0; i < passes; i++)
{
$.ajax({
type: 'POST', url: 'do.php?p=' + i, data: $('#form" . $i . "').serialize(),
success: function(data){
$('#update" . $i . "').append(data);
}
});
}
</script>
As this can iterate a few times, I was looking to execute a script for when the looping (i.e. the function itself) has finished.
As this isn't anything too snazzy, I thought it would be a simple case of adding if(i == passes -1) { alert('test');}if(i == passes -1) { alert('test');} to the end of the loop, like this:
for (i = 0; i < passes; i++) {
$.ajax({
type: 'POST', url: 'do.php?p=' + i, data: $('#form" . $i . "').serialize(),
success: function(data){
$('#update" . $i . "').append(data);
}
});
if(i == passes -1) { alert('test');}
}
....but it loads this as soon as the page loads, before the loop.
Likewise, adding a simple function after the loop acheives the same result, too.
I would have thought (but I'm quite fresh at JS) that it would complete a loop before attempting to execute the second instance of 'i', but it doesn't seem to do so - the page acts like all of the requests are sent instantly, completing the loop, executing the code, then allowing the functions within 'success' to echo back in their own time. This seems even more evident in that sometimes it will append the results for the second iteration of i before the first.
Questions...
1) Have I made an error in how I've constructed the loop, considering the purpose?
2) Why does the loop seem to execute code after the loop when it seems like it is still processing the loop itself?
What I'm trying to achieve
Each loop should perform a MySQL query, return the function's HTML output, then print it before moving on to the next. It does do this 99% correct, just with the occasional problem of it not always appending in order.
After all loops have completed and appended to the container, I would like to run some code to confirm that the operation is complete.
Many Thanks in advance, hope this is clear enough
This is a "Promise" based solution to your problem.
First, decompose each pass into a function that does one unit of work:
function makePass(i) {
return $.ajax({
type: 'POST', url: 'do.php?p=' + i, data: $('#form' + i).serialize()
}).then(function(data) {
$('#update' + i).append(data);
});
}
Then you can make a general purpose function that pseudo-recursively makes the desired number of calls to the supplied function, calling it each time with the current pass number, and finally returning a new "resolved" promise once every pass has been completed:
function makeNPasses(f, n) {
var i = 0;
return (function loop() {
if (i < n) {
return f(i++).then(loop);
} else {
return Promise.resolve();
}
})();
}
You can then register a handler to be invoked once everything is done:
var passes = Math.ceil($max / $offset);
makeNPasses(makePass, passes).then(function() {
console.log("All done!");
});
Javascript has an async flow.. It doesn't wait for the above request to get the data from somewhere... Instead it just keeps firing statements in a row.
To escape this, there are 3 options.
The ideal approach is to make 1 single http request to server and get all data in form of json array. This will be more efficient, easy and time-saving and also follows the best practices.
Make an async call of ajax. You will get the good information about it in this answer jQuery ajax success anonymous function scope. But again.. Callbacks are recommended and not doing async false. Instead .when() or .then() is easier.. because ultimately they too are callbacks.
Recursive Function can help you through such kind of tasks. It is a dirty approach because ES5 Doesn't let you iterate much deeper using recursive functions. But ES6 does allow it to be 10000 iterations. But its not a good approach. It increases overhead and bottleneck your page load.
AJAX calls are asynchronous and therefore they cannot succeed before the loop ends (as JavaScript is non blocking). In fact what you are wanting to do is to perform action after AJAX calls, not just after the loop (which is synchronous) so I would suggest either using Promise or chaining or aggregating success events like below:
var passes = Math.ceil($max / $offset);
for (i = 0; i < passes; i++) {
$.ajax({
type: 'POST',
url: 'do.php?p=' + i,
data: $('#form' + i).serialize(),
success: function(data){
$('#update' + i).append(data);
resolve();
},
error: reject
});
}
var resolved = 0;
function resolve() {
if(++resolved >= passes) {
alert('test');
}
}
function reject() {
alert('One of AJAX requests failed');
}

Making an ajax request multiple times

I have three "sources," each of which needs to have an ajax call made. However, because Ajax is asynchronous, I can't just put it in a for loop. At the same time, I can't do async: false because it's bad to have the browser hang.
Thus, I decided to have the Ajax be called multiple times in it's success callback, and construct a kind of artificial loop. Problem is, it's not working properly (I'll explain the error later on in the question). Here's my relevant code.
counter: 0,
load: function(source, start_month, end_month, start_year, end_year) {
start_month = parseInt(start_month) + 1;
end_month = parseInt(end_month) + 1;
var parseDate = d3.time.format("%Y-%m-%d").parse;
if(source == 0) {
$.ajax({
dataType: "json",
url: ...
data: {
...
},
crossDomain: true,
success: function(raw_data) {
posts.counter++;
if(posts.counter < 4) {
alert(posts.counter);
posts.load(source, start_month, end_month, start_year, end_year);
} else {
alert("plot graph");
}
}
});
}
...
This entire code block exists inside a posts closure. Just a couple of questions:
Is this good style? Is there a more efficient way to go about doing this?
For some reason the alert is only firing twice... shouldn't it
be firing 3 times with 1, 2, and 3?
I suggest using JS promises (aka deferred objects). Look into jQuery's when and then functions (http://api.jquery.com/jquery.when/ and http://api.jquery.com/deferred.then/). You can use deferred objects to make 3 asynchronous calls and wait to process the data until all 3 calls return.

overwrite javascript variable from ajax call [duplicate]

This question already has answers here:
How do I return the response from an asynchronous call?
(41 answers)
Closed 8 years ago.
I want to overwrite the status variable from the ajax call.. i tried in this way but its not overwriting the status variable... Please help me.
function myfunction()
{
var status = "";
$.ajax({
type: 'POST',
url: "<?php echo base_url() ?>login/ajaxsessioncheck/",
success: function(data)
{
status="new value";
}
});
alert(status)
return status;
}
A possible solution could be something like,
function ajaxsessioncheck(myCallback)
{
var status = "";
$.ajax({
type: 'POST',
url: "<?php echo base_url() ?>login/ajaxsessioncheck/",
success: function(data)
{
//status="new value";
myCallback(data);
}
});
// alert(status)
// return status;
}
So what you probably want to do is,
if(ajaxsessioncheck()){
//then session still exists
}
but you should actually do something like,
ajaxsessioncheck(function(data){
status = "new value";
alert(status);
//go to next page
//or do next action that is only allowed if session still exists
});
Also the thread mentioned in the comment by Arun P Johny ,How do I return the response from an asynchronous call? ,provides a thorough explanation of how to tackle this specific problem and the issues related to the synch and asynch behaviour of ajax requests.
You're lacking basic understanding of how AJAX calls work. AJAX calls are asynchronous. Your code keeps executing while the call is being processed. The code inside your success callback will only execute after the current executing code finishes (when javascript relinquishes control back to the browser) and when the AJAX request is finished. If you put the alert after status = "new value" you'll see it gets the right value.
FIDDLE
An ajax call is Async by default so when you return status the new value for status is probably not set. Either make your ajax call synchronous or define your status variable in the outer scope and then just set it inside the success callback.
var status = "";
alert(status);
function ajaxsessioncheck(){
$.ajax({
type: 'POST',
url: "<?php echo base_url() ?>login/ajaxsessioncheck/",
success: function(data){
status="new value";
alert(status);
// write code for additional functionality that needs to be executed after the new value has been set for status here.. of just make a call to another function that handles further operations..
doSomethingWithNewStatus();
}
});
}
ajaxsessioncheck();
function doSomethingWithNewStatus() {
alert("Here is your new status " + status);
}

stop executing another before one gets completed in jquery

I am using $.ajax inside the for loop in jquery.
for(var i=0; i < 2; 1++)
{
$.ajax({
url :"/models/getdata"+i
dataType:"json"
success:function(data)
{
}
});
}
before going success function for i=0; i=1 comes and performing the ajax request for this value. i want next iteration need to wait until current one gets completed.
that means one ajax request successfully completed and then it will increment the i value then start the next iteration.
consider another scenario also
function A()
{
$.ajax code
}
/////another operation
same here function A() need to wait until its ajax post get completed. but in my case it goes to next code start executing how can i stop further execution before current function gets completed. like sleep concept in C#
how can i do this ?
Thanks,
Siva
A simple generic solution :
var i=0;
(function doOne(){
$.ajax({
url :"/models/getdata"+i
dataType:"json"
success:function(data){
if (++i<2) doOne();
}
});
})();
For your second case, you may use the deferred returned by $.ajax :
function A() {
return $.ajax(...)
}
A().done(function(){
// some code executed after the ajax call finished
});
set async: false in the ajax call
This method could be the best way to handle this problem.
$.when(*AJAX CALL*)
.then(function() { // Function to run after ajax is completed
});
By default, all requests are sent asynchronously when we use $.ajax. By asynchronously, I meant, web applications can send data to, and retrieve data from, a server (in the background) without interfering with the display and behavior of the existing page. But you want execution to be stopped until your request is complete so set async to 'false'.
Use this -
$.ajax({
url :"/models/getdata"+i
dataType:"json",
async: false,
success:function(data)
{
}
});

JavaScript Synchronous Ajax Request Idiosyncrasies

I'd like to preface this with an apology if I'm doing things in a "weird" way, as I'm primarily a C developer and am solving this AJAX problem the way I would in C.
I have a script that will be connecting to a "push server" that waits until a message is available, then sends only that one message and breaks the connection. The client must then reestablish the connection to listen for future messages.
I tried to do this by implementing a synchronous AJAX call within an asynchronous callback, and it works except it appears the DOM (maybe? I'm showing my ignorance of JS here) will block until all calls are complete.
I do not know how to do it with purely asynchronous calls as I do not want to end up exhausting the stack by having a callback calling a callback each time.
This is the code:
$.ajax({
url: './recoverDevice',
data: JSON.stringify(requestData),
dataType: 'json',
type: 'POST',
success: function(j)
{
console.log(j);
if (j.success)
{
//Indefinitely listen for push messages from the server
var loopMore = true;
while(loopMore)
{
$.ajax({
async: false,
url: './getPendingMessage',
dataType: 'json',
type: 'POST',
success: function(j)
{
//alert(j.message);
$("#progressBox").append("<li>" + j.message + "</li>");
loopMore = !j.complete;
}
});
}
}
else
{
$("#errorBox").show();
$("#errorBox").text(j.errorMessage);
}
}
});
Now, logically, this code should work. Within an asynchronous function, I loop over a synchronous JS call, each time I get a message I will append it to the DOM, and only when the server tells me there will be no more messages do I exit the loop, ending the asynchronous thread and completing the task.
The problem is that the DOM access appears to be all coalesced once all messages have been received. i.e. the appends only happen once all messages have been received and the asynchronous thread has exited.
The commented out alert was a test - it works perfectly. I get a message box after each and every notification, and it pauses correctly until the next message (with the rest of the code as-is).
I'm guessing this is my browser (Chrome) doing some magic to protect against race conditions by not allowing DOM manipulation until the asynchronous thread has exited? Or am I way off the mark and barking up the wrong tree here?
Getting rid of the loop and setting async to true makes the first message be received properly (no problems there), but obviously no messages thereafter.
Obviously I could do something like this:
function GetMessage()
{
$.ajax({
async: true,
url: './getPendingMessage',
dataType: 'json',
type: 'POST',
success: function(j)
{
$("#progressBox").append("<li>" + j.message + "</li>");
if (!j.complete)
{
GetMessage();
}
}
});
}
But that would result in a stack overflow over time (no?).
An obvious solution would be to use asynchronous calls here too, but to signal a while loop to pause and continue with new calls via some sort of synchronization primitives, but appears that JS does not have signalling primitives?
Figured this one out - I don't know why I didn't see this before but my latter code fragment works perfectly. I didn't realize it at the time of posting, but it can't overflow the stack because each time it runs it launches an async call and exits - so the stack frame is never more than 2 or 3 deep. The asynchronous calls are managed externally and won't be on the stack, so each time it starts over.
I'd still appreciate any input on why the first method (synchronous code in asynchronous call) didn't/wouldn't work.

Categories

Resources