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.
Related
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
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.
Instead of request each module per line of code, I'm saving all the modules' string page in an array of modules,
var modules = ['mod1.html','mod2.html', ... , 'modN.html'];
and then pass to a function that suppose to put all the loaded modules into the modules-panel div at once.
function loadModules(modules, callback){
var content = [];
for(var i = 0; i < modules.length; i++){
$('#modules-panel').load(modules[i],function(){
content.push($('#modules-panel').html());
});
}
callback();
}
The issue is that only the last module appears where it should be.
The function should save the loaded module ( page ) into a stack repeatedly and then append all the modules in the page at once.
Given that you want to get the HTML from these pages, it would be better to use $.ajax directly and then read the response from that instead of repetedly filling an extraneous element.
Also, your callback() logic is flawed. You need to call that once all the AJAX requests have completed. For that you can use $.done. Try this:
function loadModules(modules, callback){
var content = [], requests = [];
for(var i = 0; i < modules.length; i++){
requests.push($.ajax({
url: modules[i],
success: function(html) {
content.push($('#modules-panel').html());
}
}));
}
$.when.apply($, requests).done(callback);
}
It should be noted that this may not be the best pattern to use as it will mean your server may be deluged with requests. If you can, I would look in to using server-side includes instead.
This is the place where the loadModules function is called:
var modules = ['loadfile.html', 'getFBinfo.html'];
loadModules(modules, function(data){
var total = '';
for(var i = 0; i < data.length; i++){
total += data[i];
}
$('#modules-panel').html(total);
});
Thanks for clarifying the issue! However the answer I've got was pretty simple!
I've taken the suggestion about ajax. The function became this:
function loadModules(modules, callback){
var content = [];
for(var i = 0; i < modules.length; i++){
$.ajax({
url: modules[i],
success: function(html) {
content.push(html);
if(content.length == modules.length){
callback(content);
}
}
});
}
}
As can be seen, I could return the callback function by comparing the length between what is passed to the function and the total possible/real length (the modules' length).
The problem now is to know what happens if the ajax request gets an error from the server.
The thing:
I have a page, which has to display undetermined number of images, loaded through AJAX (using base64 encoding on the server-side) one by one.
var position = 'front';
while(GLOB_PROCEED_FETCH)
{
getImageRequest(position);
}
function getImageRequest(position)
{
GLOB_IMG_CURR++;
$.ajax({
url: urlAJAX + 'scan=' + position,
method: 'GET',
async: false,
success: function(data) {
if ((data.status == 'empty') || (GLOB_IMG_CURR > GLOB_IMG_MAX))
{
GLOB_PROCEED_FETCH = false;
return true;
}
else if (data.status == 'success')
{
renderImageData(data);
}
}
});
}
The problem is that images (constructed with the renderImageData() function) are appended (all together) to the certain DIV only when all images are fetched. I mean, there is no any DOM manipulation possible until the loop is over.
I need to load and display images one by one because of possible huge number of images, so I can't stack them until they all will be fetched.
Your best bet would be to restructure your code to use async ajax calls and launch the next call when the first one completes and so on. This will allow the page to redisplay between image fetches.
This will also give the browser a chance to breathe and take care of its other housekeeping and not think that maybe it's locked up or hung.
And, use async: 'false' is a bad idea. I see no reason why properly structured code couldn't use asynchronous ajax calls here and not hang the browser while you're fetching this data.
You could do it with asynchronous ajax like this:
function getAllImages(position, maxImages) {
var imgCount = 0;
function getNextImage() {
$.ajax({
url: urlAJAX + 'scan=' + position,
method: 'GET',
async: true,
success: function(data) {
if (data.status == "success" && imgCount <= maxImages) {
++imgCount;
renderImageData(data);
getNextImage();
}
}
});
}
getNextImage();
}
// no while loop is needed
// just call getAllImages() and pass it the
// position and the maxImages you want to retrieve
getAllImages('front', 20);
Also, while this may look like recursion, it isn't really recursion because of the async nature of the ajax call. getNextImage() has actually completed before the next one is called so it isn't technically recursion.
Wrong and wrong. Don't user timers, don't chain them. Look at jQuery Deferred / when, it has everything you need.
var imgara = [];
for (image in imglist) {
imgara[] = ajax call
}
$.when.apply($, imgara).done(function() {
// do something
}).fail(function() {
// do something else
});
Try using setInterval() function instead of while().
var fetch = setInterval(loadImage, 2000);
function loadImage(){
position= new position; //Change variable position here.
getImageRequest(position);
if(!GLOB_PROCEED_FETCH){
clearInterval(fetch);
}
}
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);
}
});
}
}