How do an array of pages can be loaded - javascript

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.

Related

Javascript: Variable value never changed in a loop

This is the code:
var link, summary;
for (var i = 0; i < json.cards[0].widgets.length; i++) {
link = json.cards[0].widgets[i].text;
var params = {
// various parameters
};
var req = http.request(params, function(res) {
res.on('data', function(data) {
summary = JSON.parse(data.toString()).content;
// Now, the process method takes "summary" correctly,
// but "link" is always the same, hence, not changing
// on every other cycle.
process(summary, link);
// That's how it would look if looped 3 times
// process('positive', 'http://www.google.com/blah');
// process('negative', 'http://www.google.com/blah');
// process('neutral', 'http://www.google.com/blah');
});
});
req.end();
}
As I commented in the code, link never changes, it takes the first value, then process() takes the same value of link all the time, while summary is always different, which is expected.
Alon Eitan helped me out by suggesting to separate it in a separate function and it worked. That's how my code looks now:
var link, summary;
for (var i = 0; i < json.cards[0].widgets.length; i++) {
link = json.cards[0].widgets[i].text;
var params = {
// various parameters
};
var sent = function(link, params){
var req = http.request(params, function(res) {
res.on('data', function(data) {
summary = JSON.parse(data.toString()).content;
process(summary, link);
});
});
req.end();
}
sent(link, params);
}
req is an asynchronous function, so there is no guarantee regarding what order it will return in with comparison to the outer loop. As such, it's almost certain in this case that the outer loop finishes before the request even returns, so link is always the same.
Instead of doing this, try using promises, or pass the value of link as a parameter to the request so that it will have the right value for each return.

How to continue ajax remaining requests if file not found?

I'm trying to script a function which takes all the css/js files marked by attribute-data and refreshes if any of the scripts have been modified on the server side. My initial attempt involved php and jquery/javascript. This new attempt is based on javascript/jquery only!
My problem is that while chaining the ajax requests to these files (for Modification date), all ajax requests stop if file not found. For example, if I rename (existing) style.css to (doesn't exist) style_.css, all the chained ajax requests get aborted, and the code doesn't continue.
var file_url = [url1, url1, url3, url4, url5];
function getLatestModificationDate(file_url){
$.when.apply($, file_url.map(function(url) {
return $.ajax({ type: 'HEAD', url: url, beforeSend: function(jqXHR, settings) { jqXHR.url = settings.url; } });
})).done(function() {
var results = [], lastModified, file_jqXHR;
//each argument passed to this callback for ajax, is of this form [data, statusText, jqXHR]
for (var i = 0; i < arguments.length; i++) {
var obj= {};
file_jqXHR = arguments[i][2]; //jqXHR
lastModified = file_jqXHR.getResponseHeader('Last-Modified');
obj['file'] = file_jqXHR.url;
obj['modDate'] = lastModified;
fileArray.push(obj);
}
mainFunction(fileArray); //the main function, not in scope for this question.
});
}
I tried adding error option in ajax after beforeSend, that didn't allow continuing of remaining ajax requests. Don't know if return ajax apply(.., ..)
could return false to skip the current request for 404, cause I don't know how to skip or return false for the ajax? Is there any quick way to check if the file exists? So that I add only existing files to the file_url array that's passed to the function getLatestModificationDate(file_url){...}
EDIT: Here's a screenshot from the Chrome-Console.
EDIT :
I found this question's answer that uses a new deffered for the ajax complete... could someone provide any simplification on how that code can be used for my question? Thanks!
var myDeferred = $.Deferred();
var origDeferred = $.ajax(...);
// if request is ok, i resolve my deferred
origDeferred.done(function() {
myDeferred.resolve.apply(this, arguments);
});
// if request failed, i also resolve my deferred
origDeferred.fail(function() {
myDeferred.resolve.apply(this, arguments);
});
You can use Try-Catch block:
for (var i = 0; i < arguments.length; i++) {
try{
var obj= {};
file_jqXHR = arguments[i][2]; //jqXHR
lastModified = file_jqXHR.getResponseHeader('Last-Modified');
obj['file'] = file_jqXHR.url;
obj['modDate'] = lastModified;
fileArray.push(obj);
}catch(e){}
}

Ajax request in for loop and order of indices

I have a problem concerning the execution of ajax requests in a for loop. I already searched the web for it and found some solutions which I already implemented to avoid running the request synchronously. Unfortunately these solutions don't provide information how to ensure, that the success block gets called in the correct order aswell.
This is my code:
for (var i = 0; i < array.length; i++) {
(function(index) {
var path = array[index].split(";")[1];
var selectedRevision = array[index].split(";")[0];
$.ajax({
url: 'svntojson.jsp?revHistoryForPath=' + path,
dataType:'text',
success:function(data){
console.log(index);
var $li = $("<li/>").text(path).addClass("ui-corner-all")
.prepend("<div class='handle'><span class='ui-icon ui-icon-carat-2-n-s'></span></div>")
.append('<button class="delete"></button>')
.append('<select class="revHistoryOptions" style="float:right;margin-right:5px;">' + data.trim() + '</select>');
$("#list").append($li);
$("#list").sortable('refresh');
$('.revHistoryOptions').eq(index).children('option[value=' + selectedRevision + ']').attr('selected', 'selected');
}
});
})(i);
}
However the order of indices can change because the one ajax request succeeds earlier. This wouldn't be a problem but I am appending some list elements in the success block and I need the exact order.
So my question is how to ensure that the success block of my ajax request will be called in the order of the for loop indices from 0 to n-1.
WRONG DESIGN : If you want a complete synchronized behavior and not involving any user interaction between iterations, you can avoid
looping and use single ajax request.
You can use a single ajax when you want it completely synchronized:
$.ajax({
url: 'svntojson.jsp?inputArray=' + array,
dataType: 'json',//Note I changed this to json to receive the array on outputs
success: function (data) {
var resArray = data;
for (var index = 0; index < resArray.length; index++) {
var res = resArray[index];
var $li = $("<li/>").text(res.path).addClass("ui-corner-all")
.prepend("<div class='handle'><span class='ui-icon ui-icon-carat-2-n-s'></span></div>")
.append('<button class="delete"></button>')
.append('<select class="revHistoryOptions" style="float:right;margin-right:5px;">' + res.data + '</select>');
$("#list").append($li);
$("#list").sortable('refresh');
$('.revHistoryOptions').eq(index).children('option[value=' + res.selectedRevision + ']').attr('selected', 'selected');
}
}
});
on the server:
process the input array and generate output for each element of array. (Psuedocode as following):
var resArray = new Array(inputArr.length);
for (var i = 0; i < inputArr.length; i++) {
var res = new res();
res.path = inputArr[i].split(";")[1];
res.selectedRevision = inputArr[i].split(";")[0];
resArray.push(res);
}
return resArray;

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.

jQuery .push into an Array in a .get call gives an empty result

Can anyone tell me why the below gives me an empty string? When I console.log(contentArray) in the $.get() callback function it shows the data but when I try to do it where it is in the code below, the result is empty.
sectionArray = [];
contentArray = [];
$(function () {
if (index == 1) {
$('menu:eq(' + (section - 1) + ') li a').each(function () {
sectionArray.push($(this).attr('href'));
});
var len = sectionArray.length;
for (var i = 0; i < len; i++) {
href2 = sectionArray[i];
$.get(href2, function (data) {
string = data.toString();
contentArray.push(string);
});
}
content = contentArray.toString();
console.log(content);
}
because ajax request ends after you call console.log() try this:
$.get(href2, function(data){
string = data.toString();
contentArray.push(string);
content = contentArray.toString();
console.log(content);
});
also do ajax request in loop is not best thing to do. that wont work as you want.
UPDATE:
also jQuery has async option set to false and your code should work but will work slow. Synchronous requests may temporarily lock the browser.
UPDATE 2
maybe try something like this(maybe not so good idea :D):
var countRequests = len;
$.get(href2, function(data){
string = data.toString();
contentArray.push(string);
countRequests = countRequests - 1;
if (countRequests == 0) {
content = contentArray.toString();
console.log(content);
// or create callback
}
});
The problem is that your $.get() ajax requests are executed asynchronously.
That is, the $.get() function returns immediately without waiting for the response, your entire for loop completes (queueing up multiple ajax requests), then your console.log() occurs at which point the array is still empty. Only after that do any of the ajax success handlers get called, regardless of how fast the ajax responses come back.
EDIT: Here is an answer from another question that shows how to do something after all the ajax calls have completed: https://stackoverflow.com/a/6250103/615754

Categories