MariettaCellars
asked on
Inconsistent Response from $.getJSON()
I have written code to retrieve one or more calendar feeds from Google. It works great with 1 calendar. But when I add a second, the data that JavaScript is processing is inconsistent. I suspect it has something to do with $.getJSON()'s synchronous nature. But in spite of my best efforts to understand it, I simply cannot figure it out.
The attached script is complete. In order to see the error in action, you can refresh the browser window that's running it and click "Show Calendars" which then displays the contents of what was retrieved in a <PRE> block using a $.dump() plugin to jQuery.
What you will notice is that with each refresh (ie the F5 key) and click of "Show Calendars", the title of the calendar retrieved will randomly flop between one of the two provided public calendars. But under no circumstances does the functionality return BOTH calendars as I have intended to do.
What's going on here?
The attached script is complete. In order to see the error in action, you can refresh the browser window that's running it and click "Show Calendars" which then displays the contents of what was retrieved in a <PRE> block using a $.dump() plugin to jQuery.
What you will notice is that with each refresh (ie the F5 key) and click of "Show Calendars", the title of the calendar retrieved will randomly flop between one of the two provided public calendars. But under no circumstances does the functionality return BOTH calendars as I have intended to do.
What's going on here?
<html>
<head>
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.6.1/jquery.min.js" type="text/javascript"></script>
<script src="http://plugins.jquery.com/files/jquery.dump.js.txt" type="text/javascript"></script>
<script>
//Global application
var jGCal = {};
jGCal.googleFeedAPI_URL = "http://ajax.googleapis.com/ajax/services/feed/load?v=1.0&callback=?&q=";
//Here is our list of calendars to retrieve along with some custom colors
jGCal.calendars = [
['https://www.google.com/calendar/feeds/8tvofvvb61r3tclqev39aev4kg%40group.calendar.google.com/public', '#00f'],
['https://www.google.com/calendar/feeds/9s45c8go4950iei5cjuqes91c4%40group.calendar.google.com/public', '#f00']
];
//Make some basic projection and parameter choices
jGCal.projection = 'basic'; //the projection type of the feed to retrieve
jGCal.parameters = '?futureevents=true&sortorder=starttime'; //additional parameters to pass to the feed API
jGCal.maxResults = 3; //max results to retrieve per calendar
//Fetch the feeds for each of the calendars in our jGCal.calendars array
jGCal.fetchCalendars = function(){
var cals = jGCal.calendars; //shorthand our syntax
for(i=0, j=cals.length; i<j; i++){
//construct final URL based on projection and parameter choices
var url = jGCal.googleFeedAPI_URL+cals[i][0]+'/'+jGCal.projection+jGCal.parameters+"&num="+jGCal.maxResults;
var lastResponse = null;
//$.getJSON is our way around cross-site script defences
$.getJSON(url,
function(data){
for(i=0; i<cals.length; i++){
//AAAARRRRRGH!!!!
jGCal.calendars[i][2] = data.responseData.feed.title; //Attach response entries to associated calendar item
}
}
);
}
};
//EXECUTE
$(document).ready(function(){
jGCal.fetchCalendars();
$('#blip').click(function(){
$('#container').html($.dump(jGCal.calendars));
});
});
</script>
<title></title>
</head>
<body>
<a href='#' id="blip">Show Calendars</a>
<pre id="container"></pre>
</body>
</html>
try this:
<html>
<head>
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.6.1/jquery.min.js" type="text/javascript"></script>
<script src="jquery.dump.js" type="text/javascript"></script>
<script>
//Global application
var jGCal = {};
jGCal.googleFeedAPI_URL = "http://ajax.googleapis.com/ajax/services/feed/load?v=1.0&callback=?&q=";
//Here is our list of calendars to retrieve along with some custom colors
jGCal.calendars = [
['https://www.google.com/calendar/feeds/8tvofvvb61r3tclqev39aev4kg%40group.calendar.google.com/public', '#00f'],
['https://www.google.com/calendar/feeds/9s45c8go4950iei5cjuqes91c4%40group.calendar.google.com/public', '#f00']
];
//Make some basic projection and parameter choices
jGCal.projection = 'basic'; //the projection type of the feed to retrieve
jGCal.parameters = '?futureevents=true&sortorder=starttime'; //additional parameters to pass to the feed API
jGCal.maxResults = 3; //max results to retrieve per calendar
//Fetch the feeds for each of the calendars in our jGCal.calendars array
jGCal.fetchCalendars = function(){
var cals = jGCal.calendars; //shorthand our syntax
for(var i=0, j=cals.length; i<j; i++){
//construct final URL based on projection and parameter choices
var url = jGCal.googleFeedAPI_URL+cals[i][0]+'/'+jGCal.projection+jGCal.parameters+"&num="+jGCal.maxResults;
var lastResponse = null;
//$.getJSON is our way around cross-site script defences
for(var i=0; i<cals.length; i++){
$.getJSON(url,
function(i) {
return function(data){
jGCal.calendars[i][2] = data.responseData.feed.title;
}
}(i)
);
}
}
};
//EXECUTE
$(document).ready(function(){
jGCal.fetchCalendars();
$('#blip').click(function(){
$('#container').html($.dump(jGCal.calendars));
});
});
</script>
<title></title>
</head>
<body>
<a href='#' id="blip">Show Calendars</a>
<pre id="container"></pre>
</body>
</html>
sorry. in the code above you also need to replace jquery.dump.js with http://plugins.jquery.com/files/jquery.dump.js.txt
ASKER CERTIFIED SOLUTION
membership
Create a free account to see this answer
Signing up is free and takes 30 seconds. No credit card required.
ASKER
iGottZ
I'm still stuck in the same boat with your proposed changes. What I SHOULD be getting is the following response. notice that it returns BOTH calendars, "Bike Monkey Public" and "Other Bikely Events".
I'm still stuck in the same boat with your proposed changes. What I SHOULD be getting is the following response. notice that it returns BOTH calendars, "Bike Monkey Public" and "Other Bikely Events".
Array (
0 => Array (
0 => "https://www.google.com/calendar/feeds/8tvofvvb61r3tclqev39aev4kg%40group.calendar.google.com/public"
1 => "#00f"
2 => "Bike Monkey Public"
)
1 => Array (
0 => "https://www.google.com/calendar/feeds/9s45c8go4950iei5cjuqes91c4%40group.calendar.google.com/public"
1 => "#f00"
2 => "Other Bikely Events"
)
)
doesnt my last code does that?
ASKER
AHA!!!
Now, could you explain to me what's going on here, and what I seem to be missing, conceptually that you seem to be able to grasp?
This worked perfectly. Thank you.
Now, could you explain to me what's going on here, and what I seem to be missing, conceptually that you seem to be able to grasp?
This worked perfectly. Thank you.
ASKER
What I don't seem to understand is why $.getJSON() doesn't have access to the variable *i* without explicitly passing it in as a parameter as you've done, when it DOES have access to my global object. What's the difference?
sure i will explain it to you.
your code was this:
my code changes involved this part of yours:
what i did:
in the for i wrote "var" before the definition of i
you should do that too because defining i globaly is an often done mistake and would ruin multiple for loops if they run at the same time. (asyncronously)
then i've wrapped the function wich you defined in your json ajax call into this:
function (i) {
return /*--code--*/
}(i)
all this does is grabbing i in the moment that json call is getting executed and putting it into the scope of the ajax onsuccess event.
its like.. the ajax calls the url.. then it takes a while to retrive that url.
in that time the for loop on top would have already called this json call again. this would change i everytime the loop runs through.
out of this i can tell you that the json call is having the scope of that for loop. so when you use a variable outside that json call you must keep an eye on variables that could change in that scope.
the wrapper does nothing else then grabbing i once and creating a new scope for the function you actually want to execute when the query is done.
so when then the function accesses i it will not access the i from the for loop, it would access the i that got defined while the json call got executed.
hope this helps you understanding my code.
your code was this:
for(i=0, j=cals.length; i<j; i++){
//construct final URL based on projection and parameter choices
var url = jGCal.googleFeedAPI_URL+cals[i][0]+'/'+jGCal.projection+jGCal.parameters+"&num="+jGCal.maxResults;
var lastResponse = null;
//$.getJSON is our way around cross-site script defences
$.getJSON(url,
function(data){
for(i=0; i<cals.length; i++){
//AAAARRRRRGH!!!!
jGCal.calendars[i][2] = data.responseData.feed.title; //Attach response entries to associated calendar item
}
}
);
}
my code changes involved this part of yours:
for(var i=0, j=cals.length; i<j; i++){
//construct final URL based on projection and parameter choices
var url = jGCal.googleFeedAPI_URL+cals[i][0]+'/'+jGCal.projection+jGCal.parameters+"&num="+jGCal.maxResults;
var lastResponse = null;
//$.getJSON is our way around cross-site script defences
$.getJSON(url,
function(i) {
return function(data){
jGCal.calendars[i][2] = data.responseData.feed.title;
}
}(i)
);
}
what i did:
in the for i wrote "var" before the definition of i
you should do that too because defining i globaly is an often done mistake and would ruin multiple for loops if they run at the same time. (asyncronously)
then i've wrapped the function wich you defined in your json ajax call into this:
function (i) {
return /*--code--*/
}(i)
all this does is grabbing i in the moment that json call is getting executed and putting it into the scope of the ajax onsuccess event.
its like.. the ajax calls the url.. then it takes a while to retrive that url.
in that time the for loop on top would have already called this json call again. this would change i everytime the loop runs through.
out of this i can tell you that the json call is having the scope of that for loop. so when you use a variable outside that json call you must keep an eye on variables that could change in that scope.
the wrapper does nothing else then grabbing i once and creating a new scope for the function you actually want to execute when the query is done.
so when then the function accesses i it will not access the i from the for loop, it would access the i that got defined while the json call got executed.
hope this helps you understanding my code.
ASKER
iGottZ
Thanks for the in-depth explanation. I understand the concept of what I was missing completely now!
Thanks for the in-depth explanation. I understand the concept of what I was missing completely now!
Open in new window
isnt that what you want?