Ajax call in loop - javascript

I know there are lot of question regarding this but still I am unable to find a proper answer which makes my code run properly.
I have one function defined to call ajax which I cannot change due to security issue. This is how I call that function
var JsonIQDetails = JSON.stringify(input);//Some input
//pram 1:MethodUrl, 2:JsonObject, 3:ReturnType, 4:SuccessCallBackFunction
InvokeAjaxCall(Url, JsonIQDetails, "json", Success);
I have array of objects (more than 500). Since JSON is getting very long so I am unable to make ajax call. Again due to security issue I can't change config file too. So JSON length cannot be increased.
I am dividing the array into small chunks of 100 and calling the method
for (i = 0, j = mainObject.length; i < j; i += chunk) {
var newSubObject = mainObject.slice(i, i + chunk);
InvokeAjaxCall(Url, newSubObject, "json", Success);
function Success(data) {
if (!data) {
alert("Failed");
break;
}
}
}
Its moving without completing the for loop and executing the next code. So I want first it to complete the for loop (Probably asynchronous)
Thanks in Advance..!!!

Ajax is by default Asynchronous, so you pretty much need to invoke the next part of your ajax call in your success function. Here is a recursive loop that takes care of that.
var ajaxRecursive = function(i, j, c){
if(i < j){
var newSubObject = mainObject.slice(i, i + chunk);
InvokeAjaxCall(Url, newSubObject , "json", function(data){
//do stuff with data
ajaxRecursive(i+=chunk, j,chunk);
});
}
}
ajaxRecursive(0, mainObject.length, chunk);
Supposing that the other variables within ajaxRecursive are defined globally.
Update description:
You can get rid of your "success" function and just create it annonymously.

Related

Reading file with Javascript into array - can't seem to make it work

I have a simple 2-column csv file that I would like my site to read and ultimately parse into an array. The data is in the following format:
Atlanta Braves, Atlanta_Braves
Baltimore Orioles, Baltimore_Orioles
Boston Red Sox, Boston_Red_Sox
etc.
The file is currently stored in the same location as my html file. I am trying to use an ajax request to pull the data from the file into an array, then parse further such that myArray[0][0] = 'Atlanta Braves'.
Here is my code:
var myArray = [];
$.ajax({
type: 'GET',
url: 'datafilename.csv',
success: function(data){processData(data);}
});
function processData(data){
myArray = data.split('\n');
for (i = 0; i < myArray.length; i++){
myArray[i] = myArray[i].split(',');
}
}
alert(myArray[0][0]);
Unfortunately, the alert only returns 'undefined'. What am I doing wrong here? Any feedback would be appreciated.
$.ajax is an asynchronous function. That means that it won't complete until sometime later, after your other synchronous code has already run. Try adding this:
function processData(data) {
// Your existing code goes here...
alert(myArray[0][0]);
}
This works because processData is only run after the AJAX call has returned. Asynchronous functions basically work like this:
var value = 1;
setTimeout(function() {
value = 2; // This won't happen for about 1 second
console.log(value); // 2
}, 1000); // Run this function in 1 second
console.log(value); // 1. This happens almost immediately, without pause

html page doesn't have a chance to render between ajax calls

I have some 'ajax' calls (really sjax I guess you could call it) and I'm trying to make them render on the page one at a time, but they aren't. They all render at the end. How can I space them out?
function getNames() {
var names = $('#thenames').val();
alert(names);
var splitnames = names.split(',');
for(var i = 0; i < splitnames.length; i++) {
var name = splitnames[i];
$.ajax({
type: 'GET',
url: '/acert/secure/people/namesservice/getnamesajax.jsp',
data: { usernames: name},
success: function(data) { $('#results').html($('#results').html() + data);},
async: false });
}
}
}
I can't risk them coming back in the wrong order so I need them to be synchronous. I put them into a for-loop, so the for-loop should give the browser a chance to render between calls, but I can't seem to make it.
Any ideas on what I'm doing wrong?
If I add an alertbox in the success function it works, but I don't want to have to babysit the operation, I just want to monitor its progress now and again.
async: false blocks the browser. It completely locks up everything, including repaints to the DOM.
I strongly strongly recommend you don't use async: false. It is extremely bad.
You might be able to use setTimeout in-between the calls, but they don't guarantee the browser will trigger a repaint.
If you set async: true you will not have this problem, but you will likely have to change your code to properly deal with asynchronous behaviour.
async false is so bad jQuery decided to remove it from the API.
Do not use async: false.
The code below will run all ajax requests as fast as possible, then append the content to #results in the correct order. DO NOT include async: false if you are using the code below.
var defArr = [];
for(var i = 0; i < splitnames.length; i++) {
defArr.push( $.ajax({...}) );
}
$.when.apply($,defArr).done(function(){
var $results = $("#results");
$results.empty();
for (var i = 0; i < arguments.length; i++) {
$results.append(arguments[i][0]);
}
});
assuming you know how many calls you make (or can include that as a parameter to the return result) you can simply fire the calls asynchronously, and make them elements in an array on your success callback. When the array gets to the expected size, just render them in sequence.
For one, you seem to have an extra curly brace there.
But more to the issue at hand, if you just want to monitor the progress would it work for you to use setTimeout?
-- update --
I think I get what you're trying to do. And if I'm not mistaken, you could refactor a little, and then use a closure and an object with the names as the keys. Something like this:
function getNames()
{
var names = $('#thenames').val();
var splitnames = names.split(',');
var myData = {};
for(var i = 0; i < splitnames.length; i++)
{
(function(name)
{ return function(){
$.ajax({
type: 'GET',
url: '/acert/secure/people/namesservice/getnamesajax.jsp',
data: { usernames: name},
success: function(data) { myData[name] = data; updateNames(); }
});
})( splitnames[i] )
}
}
What this basically does is that it sets up a bunch of ajax calls right away, that weird bit in the middle with the (function(){})() makes sure you don't end up fetching the last value that name gets set to when the loop finishes. Everything gets saved to myData, I figured once everyone is loaded you could check to see if all the names you have in splitnames are in myData with the updateNames function. Something like
var count = 0;
for ( var i = 0; i < splitnames.length; i++ )
{
count += myData[splitnames[i]] != null ? 1 : 0;
}
if (count == splitnames.length)
{
// write the names to the screen
}
Does that make sense?
But, to be honest, the best approach would probably be to change the getnamesajax.jsp so that it accepts all the names, and then gives you back the info you need in the order you need. If that was an option, that would be best since you would only need to make one ajax call.

Comparing two arrays in Javascript for different values

Javascript gurus, I need your help.
I need to compare two different arrays and check for different values. The values are coming from the same form multi select element. I tried getting a list of current values (cacheTermList) and checking for new value on change (newTermList). The idea is I want to pass an id to an ajax call if a new value was input, and return some data to the screen.
Code:
var cachedTermList = $('select#edit-categories').val();
if (cachedTermList == null) {
var cachedTermList = new Array();
}
$('select#edit-categories').chosen().change(function() {
var newTermList = $('select#edit-categories').val();
if (cachedTermList != null) {
for(var i = 0; i < newTermList.length; i++) {
alert(newTermList[i]);
if (!($.inArray(newTermList[i], cachedTermList))) {
$.ajax({
type: "GET",
url: "/classifieds/js/term/" + newTermList[i],
success: function(data){
//$('div#term-help-text').html(data);
cachedTermList.push(newTermList[i]);
alert(cachedTermList);
}
});
}
}
} else {
}
});
Bear with me, I don't tend to work with Javascript too often. I was trying to get a current list of values by setting cachedTermList on load, then when the select changes, set a newTermList to the new value of the field, then loop it, and check for a value in that list that is not in the cached list.
While I could see things happen, and dump both term lists and see different values, for the life of me I could not get it to push the found value to the cached list so that the next time the element changes, it doesn't keep sending the same value to the ajax call again and again. After the .push() executes, it just adds ',,,' without values. Where am I going wrong?
It is the typical closure - loop problem. All success callbacks reference the same i. At the time the callbacks are executed, the loop already finished and i will have the value newTermList.length + 1, so newTermList[i] will return undefined.
You have to capture the index or the value by introducing a new scope, which can be done by calling a function (JavaScript has no block scope).
Update: Another problem is that $.inArray does not return a boolean value, but the index of the element or -1. So you have to compare the return value against -1.
$('select#edit-categories').chosen().change(function() {
// ...
for(var i = 0; i < newTermList.length; i++) {
if ($.inArray(newTermList[i], cachedTermList) === -1) {
addTerm(newTermList[i], cachedTermList);
}
}
//...
});
function addTerm(term, target) {
$.ajax({
type: "GET",
url: "/classifieds/js/term/" + term,
success: function(data){
//$('div#term-help-text').html(data);
target.push(term);
alert(target);
}
});
}
Also keep in mind that all Ajax calls will basically be executed at the same time. The loop does not wait until one call finished. If you want to execute the calls one at a time, you can use jQuery's Deferred objects.
You are using ajax in a for loop and pushing the newTermList item in its success handler. Since ajax is async by default the loop is not going to wait for the ajax request to get completed. Try to put the pushing code outside the ajax call. But this will not work if the ajax call fails, may be you dont want to add the item into cache when the ajax call fails.
Try something like this
for(var i = 0; i < newTermList.length; i++) {
alert(newTermList[i]);
if (!($.inArray(newTermList[i], cachedTermList))) {
$.ajax({
type: "GET",
context: i,//Here I am setting the value of i into context which can be used in the success handler using this keyword
url: "/classifieds/js/term/" + newTermList[i],
success: function(data){
//$('div#term-help-text').html(data);
cachedTermList.push(newTermList[this]);
alert(cachedTermList);
}
});
}
}

mulitple JSON feeds in an array

I am trying to pull 5 separate JSON feeds and have it looping where every individual has a nested getJSON.
var feedList [feed1,feed2,feed3,feed4,feed5];
for (feed in feedList) {
var index = 0;
$.getJSON(feedList[feed], function(data) {
$.each(data.items, function(i, obj) {
$('li').append(obj.title + '=' + index + '<br>');
});
index++;
}
}
For some reason in firebug it shows that I go through the for in loop then pull the feed successfully then completely bypass the anything inside the .getJSON loop. It doesn't go back into .getJSON loop until all the feeds are pulled. This messes up the order of the items being pulled and also the order is random every time I refresh the page. (e.g. feed2 is listed first then feed4 next)
I've also tried doing a regular for loop instead of a for in loop but it still changes nothing.
Can anyone help me?
Send the next request on success of the previous one by using a recursive function
function getFeed(feed){
$.getJSON(feed, function(data) {
$.each(data.items, function(i, obj) {
$('li').append(obj.title + '=' + index + '<br>');
});
if(feedList.length > index + 1)
getFeed(feedList[++index]);
}
}
// start
getFeed(feedList[0]);
The order would be random because $.getJSON is an asynchronous request for a file. Put simply each request will take some time to complete, and your function (with the each call) will only be called for each request once each request completes respectively.
The problem arises in the fact that you cannot control which requests will return in which order. Since the all are requested at the same time.
You could get the feeds in order by waiting until each request completes before trying the next request:
var feedList [feed1,feed2,feed3,feed4,feed5];
var index = 0;
function getFeed(index) {
$.getJSON(feedList[index], function(data) {
$.each(data.items, function(i, obj) {
$('li').append(obj.title + '=' + index + '<br>');
});
if (index < feedList.length - 1)
getFeed(++index);
});
}
getFeed(0);
Try using a regular loop, combined with self executing anonymous functions. You may need to explicitly disable asynchronicity for ajax, though.
for ( var i = 0, l = feedList.length; i<l; ++i ) {
(function() {
// $.getJSON code
})();
}
Actually the inner function is probably useless. You can disable asynchronicity with
$.ajaxSetup( { "async": false } );

Javascript/jQuery variables not giving expected values

Like others before me I'm struggling with scope in Javascript. (That and trying to read the darn stuff). I have checked some of the previous threads on this question but I cant seem to get them to apply correctly to my issuue.
In the example below, I want to manipulate the values in the tagsArr array, once the array has been fully populated. I declared the tagsArr variable outside the scope of the function in which it is populated in order to access it globally. But the variable doesn't seem to have the scope I expect - tagsArr.length is 0 at the point where I call output it to console on line 16.
$(function(){
var apiKey = [myapikey];
var tags = '';
var tagsArr = new Array();
$.getJSON('http://api.flickr.com/services/rest/?&method=flickr.people.getPublicPhotos&api_key=' + apiKey + '&user_id=46206266#N05&extras=date_taken,tags&format=json&jsoncallback=?', function(data){
$.each(data.photos.photo, function(i, item) {
var photoID = item.id;
$.getJSON('http://api.flickr.com/services/rest/?&method=flickr.photos.getInfo&api_key=' + apiKey + '&photo_id=' + photoID + '&format=json&jsoncallback=?', function(data){
if (data.photo.tags.tag != '') {
$.each(data.photo.tags.tag, function(j, item) {
tagsArr.push(item.raw);
});
}
});
tags = tagsArr.join('<br />');
console.debug(tagsArr.length);
});
$('#total-dragged').append(data.photos.total);
$('#types-dragged').append(tags);
});
});
Your calls to getJSON are asynchronous. Hence all the calls to the inner getJSON will still be outstanding by the time the console.debug line is reached. Hence the array length is still 0.
You need to run some extra code once the final getJSON call has completed.
$(function(){
var apiKey = [myapikey];
var tags = '';
var tagsArr = new Array();
$.getJSON('http://api.flickr.com/services/rest/?&method=flickr.people.getPublicPhotos&api_key=' + apiKey + '&user_id=46206266#N05&extras=date_taken,tags&format=json&jsoncallback=?', function(data){
var totalExpected = data.photos.total;
var totalFetched = 0;
$.each(data.photos.photo, function(i, item) {
var photoID = item.id;
$.getJSON('http://api.flickr.com/services/rest/?&method=flickr.photos.getInfo&api_key=' + apiKey + '&photo_id=' + photoID + '&format=json&jsoncallback=?', function(data){
if (data.photo.tags.tag != '') {
$.each(data.photo.tags.tag, function(j, item) {
tagsArr.push(item.raw);
totalFetched += 1;
if (totalFetched == totalExpected)
fetchComplete();
});
}
});
function fetchComplete()
{
tags = tagsArr.join('<br />');
console.debug(tagsArr.length);
}
});
$('#total-dragged').append(data.photos.total);
$('#types-dragged').append(tags);
});
});
This works assuming the total number of photos doesn't excede the default 100 per page, other wise you would need to tweak it.
That said I don't think using .each to fire off loads of getJSON requests makes a great deal of sense. I would refactor it so that only one call to getJSON is outstanding at any one time. Have the callback of one issue the next getJSON for the next photo until all have been pulled then do your completed code.
$.getJSON is asynchronous (the a in ajax). That means that by the time you get to console.debug(), getJSON is still getting. You'll need to do some extra work in the JSON callback.
The reason for this is that getJSON is an asynchronous request. after the call to $.getJSON, the javascript engine will move immediately on to the following two lines of code, and will output the length of your array, which is by then, zero-length. Not until after that does the getJSON request receive a response, and add items to the array.
The getJSON function is asynchronous, so when you call the debug function the array is still empty because the requests are not completed. Use the $.ajax function and set async:false and it will work.
$.ajax({
type: "GET",
url: 'http://api.flickr.com/services/rest/?&method=flickr.photos.getInfo&api_key=' + apiKey + '&photo_id=' + photoID + '&format=json&jsoncallback=?',
dataType: "json",
async:false,
success:function(data){
if (data.photo.tags.tag != '') {
$.each(data.photo.tags.tag, function(j, item) {
tagsArr.push(item.raw);
});
}
}
});
This isn't a scope issue - the problem is that getJSON is asynchronous, so it continues executing immediately after sending the request to flickr. By the time the browser executes console.debug the request hasn't returned and you haven't finished handling the response (and therefore haven't pushed any items into the array yet).
To solve this, find all the code that should only be executed when the array is full and move it into your getJSON callback method:
if (data.photo.tags.tag != '') {
$.each(data.photo.tags.tag, function(j, item) {
tagsArr.push(item.raw);
});
tags = tagsArr.join('<br />');
console.debug(tagsArr.length);
$('#total-dragged').append(data.photos.total);
$('#types-dragged').append(tags);
}
You may want to check the answer to this question I posted. There is some good information on scope issues in javascript.

Categories