put $.getJSON(url, function(data) in a loop but yield wrong result - javascript

I run several calls to the Instagram API by using a loop but it yields weird result that I couldn't understand.
for(var i = 0; i < tags.length; i++){
document.write('search: ' + i + 'round');
document.write(tags[i] + ' ');
document.write('<hr />');
var url = 'https://api.instagram.com/v1/tags/'+ tags[i] +'/media/recent?&client_id=5a7b13571ced47418dd539e6fc97a67f&count='+count+'&callback=?';
$.getJSON(url, function(data) {
console.log(data.data.length);
if(data.data.length === 0){
//$('ul.images').append('<li>No results</li>');
} else {
$.each(data.data, function(index, value){
//console.log(value);
var imgUrl = value.images.low_resolution.url,
imgUser = value.user.username,
imgLink = value.link;
it.push(imgLink);
document.write(value.link + '<br />');
//$('ul.images').append('<li><img src="'+imgUrl+'"/></li>');
});
}
document.write('---------------------' + '<br />');
});
}
When I search three tags, cat, dog, pig, the result is:
search: 0roundcat
search: 1rounddog
search: 2roundpig
5 instagram image links
---------------------
5 instagram image links
---------------------
5 instagram image links
---------------------
So it seems the it first loop the three document.write() statement and then getting into $.getJSON() statement.
Any thoughts?

If your question is why the "i + round + tag" output comes before the output with the links, that is because getJSON() is asynchronous, i.e. it starts a request and returns immediately.
Then, after the for loop is finished, the execution has returned to the event loop and the results of the asynchronous requests have arrived, they are printed in the order that they arrived.
There are several ways to get around this, one would be something like this:
var tags = ['cat','dog','pig'];
function start_next_request() {
var tag = tags.shift();
var url = ...
$.getJSON(url, function (data) {
...
start_next_request();
}
}
start_next_request();

Related

getJSON not fetching data

My code below is not fetching data from data.js
The page works fine when the JSON data is hard coded into the page.
<head>
<title>Test Page</title>
<script>
function jsontest() {
var text;
$.getJSON("data.js", function(result) {
text = result;
});
var obj = JSON.parse(text);
document.getElementById("content").innerHTML =
obj.name + "<br>" +
obj.street + "<br>" +
obj.phone;
}
</script>
</head>
<body onload="jsontest();">
<h1>Testing Page</h1>
<p id="content"></p>
</body>
My data looks like this
{"name":"John Johnson","street":"Oslo West 16","phone":"555 1234567"}
Am I making a simple nooby mistake?
The first thing I notice is that you're using getJSON in sync mode. This won't work since it's executed asynchronous. You need to place your logic inside finish handler
function jsontest() {
var text;
$.getJSON("data.js", function(result) {
text = result;
var obj = JSON.parse(text);
document.getElementById("content").innerHTML =
obj.name + "<br>" +
obj.street + "<br>" +
obj.phone;
});
}
In your code, by the time you do
var obj = JSON.parse(text);
method getJSon didn't return yet so text contains the default value. It sends the request to the server and continue method flow without waiting for the result.
That's what the handler is for: to put logic that needs to be executed when request is complete.
$.getJSON is an async call - so when you try and run JSON.parse outside the callback, it won't work since the call is still in progress. Move your code to the callback and you'll be good:
function jsontest() {
$.getJSON("data.js", function(result) {
var text = result;
var obj = JSON.parse(text);
document.getElementById("content").innerHTML =
obj.name + "<br>" +
obj.street + "<br>" +
obj.phone;
});
//Anything after the `getJSON` call is executed immediately.
//$.getJSON is still in progress when these lines are executing
}
Javascript is single-threaded and non-blocking, as a result, while the ajax call is being executed the program counter will continue. Thus will be parsing something which is undefined.
If you put the code beneath the ajax callback inside it, it will work just fine.
You have to put your code inside the callback, or you wont have the text var populated:
function jsontest() {
var text;
$.getJSON("data.js", function(result) {
text = result;
var obj = JSON.parse(text);
document.getElementById("content").innerHTML =
obj.name + "<br>" +
obj.street + "<br>" +
obj.phone;
});
}

Cache each JSON search query with localStorage

THE PROMPT: We have a search that connects to an JSON API url. The search query is inside the url, and the API generates a new JSON file for every search term. We also cannot rely on the browser to cache for us, and we can't use PHP or server side caching; we need to use HTML5 LocalStorage (and we don't care that IE7 can't use it)
We need to cache every new JSON file for every new search. We want to cut down on requests per minute, so we want to use a cached version of the JSON file for repeated search terms.
WHERE I'M STUCK: What has made this difficult is caching a JSON file for each new/different search term. I have been able to cache the first search, but then all subsequent searches use the same cached JSON.
We need help rewriting this so each time a new search is made, it checks to see if the term was searched for previously and if so, grabs the corresponding JSON file. Then of course if the search term is new then cache a new JSON file for that specific search term.
WHAT I'VE TRIED: In my research I've seen a lot of very complicated solutions and I can't seem to get my head completely around all of it, some of these solutions almost worked, I think I just need a better explanation for this specific case.
I think this is the answer but I don't know how to apply it to my situation: jQuery deferred ajax cache
This is crazy and it almost works, it writes into the console when it recognizes that I've searched the same thing again, and it does stop a new request, but unfortunately the cached JSON isn't there, it returns no results.
Caching a jquery ajax response in javascript/browser
WHAT I HAVE SO FAR:
MY PSUEDO CODE:
var searchTerm = WHATEVER IS TYPED INTO THE SEARCHBOX
// The JSON file
var url = 'https://api.example.com/fake/json/path/{'+searchTerm+'}';
// Local Storage Caching Promise
var cachedData = localStorage.getItem("cachedData"),
def = $.Deferred();
if (!cachedData) {
def = $.getJSON(url, function(data) {
cachedData = data;
localStorage.setItem("cachedData", JSON.stringify(cachedData));
});
}
else{
cachedData = JSON.parse(cachedData);
def.resolve();
}
def.done(function() {
var resultHTML = '';
for(var i = 0; i < Object.keys(cachedData.things).length; i++){
$.each(cachedData, function(index, node){
resultHTML += '<li>'
resultHTML += '<h1>' + node[i].name + '</h1>';
resultHTML += '</li>';
});
}
$('div#results').html(resultHTML);
});
EXAMPLE JSON:
{
"things": [
{
"type": "thing",
"username": "randoguy",
"name": "name001",
},
{
"type": "thing2",
"username": "randoguy2",
"name": "name002",
},
...
Thank you #Ian for providing the hints to my answer!
var searchTerm = WHATEVER IS TYPED INTO THE SEARCHBOX;
// The JSON file
var url = 'https://api.example.com/fake/json/path/{'+searchTerm+'}';
// BABAM! Right here, SearchTerm + "-cachedData" gets unique cached data
var cachedData = localStorage.getItem(searchTerm + "-cachedData"),
def = $.Deferred();
if (!cachedData) {
def = $.getJSON(url, function(data) {
cachedData = data;
// BABAM! And here is where the unique cachedData is set! SearchTerm + "-cachedData"
localStorage.setItem(searchTerm + "-cachedData", JSON.stringify(cachedData));
});
}
else{
cachedData = JSON.parse(cachedData);
def.resolve(cachedData);
}
def.done(function(data) {
var resultHTML = '';
for(var i = 0; i < Object.keys(data.repositories).length; i++){
$.each(data, function(index, node){
resultHTML += '<li>'
resultHTML += '<h1>' + node[i].name + '</h1>';
resultHTML += '<p>' + node[i].owner + '</p>';
resultHTML += '</li>';
});
}
$('div#results').html(resultHTML);
});
Where would I be without StackOverflow. Thank you all!

Nested JSON fetch using jQuery

I am trying to create an RSS Feed kind of Message display from Yammer.
<script type="text/javascript">
var cleanit = null;
$(document).ready(function(){ cleanit = setInterval('callYammer()', 50);});
function callYammer(){
clearInterval(cleanit);
$.getJSON("./yammer.feed?request=messages",function(json) {
var objYammer = $("#yammerFeed");
objYammer.html('');
$.each(json.messages, function(i, m) {
if(!m.replied_to_id && m.body.plain){
var data = "<li>" + m.body.plain;
$.getJSON("./yammer.feed?request=users&userid="+m.sender_id,function(jsonUser) {
//alert(jsonUser.full_name);
data = data + " - "+jsonUser.full_name;
});
data = data + "</li>";
objYammer.append(data);
}
});
});
return false;
}
</script>
I want to display Message along with it's Username.
But in the end, from firebug debugger, what I see is the inner JSON data is not getting appended as I expected.
Though the calls are hitting and data is coming from the call, the
data = " - "+jsonUser.full_name;
is getting executed after all JSON calls for Users.
How do I append Username from inner JSON call to main JSON data?
You call the lines
data = data + "</li>";
objYammer.append(data);
in the code following your inner getJSON AJAX call, but that probably results in these lines being executed before the AJAX request has finished. Put the code INTO the inner AJAX success function to make sure it is fired only after the result is available.
function callYammer(){
clearInterval(cleanit);
$.getJSON("./yammer.feed?request=messages",function(json) {
var objYammer = $("#yammerFeed");
objYammer.html('');
$.each(json.messages, function(i, m) {
if(!m.replied_to_id && m.body.plain){
var data = "<li>" + m.body.plain;
$.getJSON("./yammer.feed?request=users&userid="+m.sender_id,function(jsonUser) {
console.log('1:'+jsonUser.full_name);
data += " - "+jsonUser.full_name + "</li>";
objYammer.append(data);
console.log('2:'+data);
});
}
});
});
Edit:
Just added the console.log() statements. What do they return?

using .ajax() in .each()

I'm trying to iterate through elements of .ball class and check if their associated url exists:
$(".ball").each(function(i){
var url;
var c_day = $(this).data('day');
var c_mon = $(this).data('mon');
var c_year = $(this).data('year');
url = c_year + "/" + c_mon + "/" + c_day + ".html";
$.ajax({
url: url,
error: function()
{
alert('file: ' + url + ' does not exist');
},
success: function()
{
alert('file: ' + url + 'EXXXXXXISTS!!!!!');
blogA[ blog_count ] = url;
blog_count++;
$(this).css("color", "red" );
}
});
});
I've done some research and read that using .ajax in .each causes a lot of problems but I couldn't wrap my head around on how to fix it.
The problem is that I get really weird results (has to do with asynchronous?). If I alert url outside of ajax, it correctly iterates through the elements. If I alert url in ajax, it spits out urls that belong to the last element of the class.
Something like this, in order to simplify your code
function successHandler(url, ball) {
return function(ret) {
alert('file: ' + url + 'EXXXXXXISTS!!!!!');
ball.css('color','red')
}
}
var balls = $('.ball'), requests = []
balls.each(function(index, ball) {
var url = ...
requests.push($.ajax({ url : url , success : successHandler(url, ball) })
})
$.when.apply($, requests).done(function() {
alert('all balls are checked')
})
Or with ES6:
const success = (url,ball)=>(ret)=>ball.css('color','red')
const balls = $('.balls')
, reqs = balls.map( (b, i) => {
const url = ...
return $.ajax({url, success:success(url,ball)})
})
$.when.apply($, reqs).done( (resps) => alert('all done'))
A Little explanation: You are blindly using this in your callback, not knowing in which context it is going to be executed. In order to work around it and has your URL into callback we are creating function that returns a function, so it will have URL of current .ball DOM object in the context.
You'll probably also need to execute code after all ajax requests are done, so using $.when is the simplest way of doing it. And we are storing all promises just for this reason.
If you aren't concerned about the order of execution of each ajax call and just want to know when they are all done and the array is fully populated, then you can get this to work by fixing your reference to this and by adding a callback function that is called when all items are done:
// this function called when the ajax calls for all balls have finished
// at this point, blogA and blog_count will be populated
function ballsDone() {
// put your code here
}
var balls = $(".ball");
var cnt = balls.length;
balls.each(function(i){
var url;
var self = $(this);
var c_day = self.data('day');
var c_mon = self.data('mon');
var c_year = self.data('year');
url = c_year + "/" + c_mon + "/" + c_day + ".html";
$.ajax({
url: url,
error: function()
{
alert('file: ' + url + ' does not exist');
if (--cnt <= 0) {
ballsDone();
}
},
success: function()
{
blogA[ blog_count ] = url;
blog_count++;
self.css("color", "red" );
if (--cnt <= 0) {
ballsDone();
}
}
});
});
Keep in mind that the ajax calls are asynchronous so the ONLY place you can use the results from the ajax call is in the success handler. You can't use the results of the ajax calls right after the .each() call in a synchronous fashion because the ajax calls have not yet finished. You must wait for the success handler to be called and when cnt success handlers have been called, then they are all done and you can then process the results.

Javascript Array loses data

I'm having trouble getting my information into an array in an ajax call, if I alert the information right after I insert it into the array it works fine, but if I do it at the end it alerts unidentified. I made sure that books is declared outside so it doesn't interfere.
var books = [];
$.ajax({
url: 'getFolderContents.php',
dataType: 'json',
success: function (data)
{
for(var i=0;i<data.length;i++) {
var amm = 0;
if(data[i].indexOf(".epub") !== -1) {
//$('#bTable').append("<td><a id = '" + data[i] + "' href = 'book.html'><img src = 'book.png' width = '100px'/><br/>" + data[i] + "</a></td>");
books.push(data[i]);
//alert(books[0]) Works if I call it from here, but not at the end.
}
}
},
error: function()
{
alert("error");
}
});
alert(books[0]);
Your
alert(books[0]);
will be executed while the Ajax call is running and therefore will not have any elements at this point of execution yet. Ajax is asynchronous - while you are doing a request to your PHP script your script continues execution.
Put all actions with books in your success function.
Another hint: As of jQuery version 1.8 you cannot longer use the parameter async: false to create a synchronous "A"jax call. You have to use the callback functions. Have a look at the docs for $.ajax
Your array hasn't lost any data; the data hasn't been put in there yet. The 'A' stands for "asynchronous", meaning your success callback hasn't run yet at the time you call the alert.
Put the alert inside your callback instead:
success: function (data)
{
for(var i=0;i<data.length;i++) {
var amm = 0;
if(data[i].indexOf(".epub") !== -1) {
//$('#bTable').append("<td><a id = '" + data[i] + "' href = 'book.html'><img src = 'book.png' width = '100px'/><br/>" + data[i] + "</a></td>");
books.push(data[i]);
//alert(books[0]) Works if I call it from here, but not at the end.
}
}
alert(books[0]);
},
Your alert is executing before the success function is called. Perhaps seeing the same code using a promise will make things clearer.
$.ajax( url: 'getFolderContents.php', dataType: "json" )
//the then function's first argument is the success handler
.then(function( data ) {
for(var i=0;i<data.length;i++) {
var amm = 0;
if(data[i].indexOf(".epub") !== -1) {
//$('#bTable').append("<td><a id = '" + data[i] + "' href = 'book.html'><img src = 'book.png' width = '100px'/><br/>" + data[i] + "</a></td>");
books.push(data[i]);
//alert(books[0]) Works if I call it from here, but not at the end.
}
alert(books[0]
});
});
I always feel this syntax makes async stuff make more sense. Otherwise this code functions exactly like Blazemonger's correct answer.
Your AJAX call is asynchronous, that's why it is undefined.
The alert at the end happens before the ajax success callback, because ajax is asynchronous.

Categories

Resources