$.post not running until all function calls are made
Pregunta
Running into a bit of a funny problem. I have each day of the week hidden upon entry to my html (class="day"
). By clicking on the Day, it expands to show everything under that day, by an AJAX call with $.post
. This is all working well.
I figure the user may want to look at the whole week at once, so I have a button (id="expander"
) to show everything. However, when I call the function getSchedule()
from within the #expander
click, it loops through everything except the $.post
in the getSchedule()
function. Then it loops through the $.post
.
$(document).ready(function()
{
$('.day').click(function(){
pass = $(this).prop('id');
getSchedule(pass);
});
$('#expander').click(function(ex){
$('.day').each(function(index){
pass = $(this).prop('id');
getSchedule(pass);
});
$('#expander').text('Hide All Days');
ex.preventDefault();
return false;
});
});
function getSchedule(elementid){
dow = elementid.substr(-1);
url = "standarddayofweek.php?d="+dow;
$theday = $("#"+elementid);
console.log("The day is "+$theday.prop('id'));
$.post(url,function(data){
$theday.children('.dayschedule').html(data);
}, "html"
)
}
The console response I get is:
The day is d1
The day is d2
The day is d3
The day is d4
The day is d5
The day is d6
The day is d7
XHR finished loading: "http://127.0.0.1/standarddayofweek.php?d=1"
In the post function, the day is d7
XHR finished loading: "http://127.0.0.1/standarddayofweek.php?d=2"
In the post function, the day is d7
XHR finished loading: "http://127.0.0.1/standarddayofweek.php?d=3"
In the post function, the day is d7
XHR finished loading: "http://127.0.0.1/standarddayofweek.php?d=4"
In the post function, the day is d7
XHR finished loading: "http://127.0.0.1/standarddayofweek.php?d=5"
In the post function, the day is d7
XHR finished loading: "http://127.0.0.1/standarddayofweek.php?d=6"
In the post function, the day is d7
XHR finished loading: "http://127.0.0.1/standarddayofweek.php?d=7"
In the post function, the day is d7
The watered down HTML:
<div id="expander">Click me to expand</div>
<div id="d1" class="day">
<div class="dayschedule"></div>
</div>
<div id="d2" class="day">
<div class="dayschedule"></div>
</div>
....
<div id="d7" class="day">
<div class="dayschedule"></div>
</div>
What is going on? I have tried adding a delay, to see if the AJAX was happening too fast, but nothing changed.
Solución
Not all browsers will behave synchronously, so async: false
is not a comprehensive solution. However, localizing the variables in getSchedule()
is.
Try this:
function getSchedule(elementid) {
var dow = elementid.substr(-1);
var url = "standarddayofweek.php?d="+dow;
var $theday = $("#"+elementid);
console.log("The day is "+$theday.prop('id'));
$.post(url, function(data) {
$theday.children('.dayschedule').html(data);
}, "html");
}
Thus, a closure is formed containing the (formal and informal) vars, which remain available to the inner function (the ajax success handler) when it fires asynchronously, even though the outer function has completed and returned.
Otros consejos
Javascript primarily runs with a single thread of execution see here. What this means to your code:
First off the .each
is just a loop. Imagine the ajax requests are sent in a for loop.
for(...)
$.post();
The ajax callback cannot run until the current execution stops. So your code will go through all of the elements, and log as it does, then the thread is released and the ajax callback can take control.
You should change your program so that you can get a list or batch of content back so that one request is only needed for several elements.
JavaScript is not multi-threaded. You're function must return before asynchronous stuff can happen.
By repeatedly calling $.post
without returning the flow of execution to the browser, you are effectively queueing up asynchronous actions which will run when your code has finished.
If, for example, your code were to never return by looping forever, none of your $.post
s would ever be invoked.
Thanks to @RobertSmith, the answer was switching from $.post to $.ajax. I just changed:
$.post(url,function(data){
$theday.children('.dayschedule').html(data);
}, "html"
)
to
$.ajax({
url: url,
async:false,
success: function(data) {
$theday.next().children('.dayschedule').html(data);
}
});
Note the async:false
; that is what required the switch.
Thanks to everyone for their comments!