Node.js http.get in loop only getting last item - javascript

I'm looping over a set of URLs trying to get their HTML, but it only works for the last one in the list. The "NOT WRITING YET" console.log fires for everything in the urls array, as expected, but the console.logs after that only fire for the last one. They are all valid URLs and "res.on('error'....." returns nothing.
Any help is greatly appreciated!
for (var z in urls) {
var getURL = urls[z];
var copyURL = 'copies/'+getURL;
if (copyURL.indexOf('/') >= 0) {
var copyURLArr = copyURL.split('/');
} else {
var copyURLArr = [copyURL];
}
var copyFile = copyURLArr.pop();
var runningDirs = '';
for (var i in copyURLArr) {
if (runningDirs.length > 0) runningDirs += '/';
runningDirs += copyURLArr[i];
if (!require('fs').existsSync(runningDirs)) {
exec('mkdir '+runningDirs);
}
}
console.log('NOT WRITING YET: '+urlPrefix+getURL);
require('http').get(urlPrefix+getURL, function(res) {
console.log(urlPrefix+getURL);
res.on('data', function(data) {
console.log(copyURL);
require('fs').createWriteStream(copyURL, {flags:'a+'}).write(data);
});
});
}

In the line console.log(urlPrefix+getURL); the value will always be for the last url because when the callback is called the values of these variables will be from the last time they got assigned which is the last iteration in the loop.
instead make the request in a separate function like
function doRequest(url, copyURL) {
require('http').get(url, function(res) {
console.log(url);
res.on('data', function(data) {
console.log(copyURL);
require('fs').createWriteStream(copyURL, {flags:'a+'}).write(data);
});
});
}
and then call this function with doRequest(urlPrefix+getURL, copyURL) then check if the problem still exists

Related

Next iteration of $.each when received AJAX-content

The question has been asked before, but it is almost four years ago and maybe there is a better solution.
I have a $.each-loop where sometimes additional data is being fetched via ajax.
I am bulding an object with the fetched data, after the loop there is a function that generates HTML from the object. The problem is that the loop finishes before the ajax data arrives. If I place an alert in the HTML-generating-function the content is loading properly.
I am searching for a solution that calls the HTML-generator-function only when the loop and all ajax calls are finished. Maybe it is a solution to count the started Ajax requests and wait if all of them are finished?
I believe jQuery deferred is the right solution for me but I do find only examples where everything stays inside the loop. Can someone help?
I have stripped down my code to the most important things:
//goes through each testplace -->main loop
$.each(jsobject, function(key, value)
{
//build object together...
for (var i = 0, numComputer = jenkinsComputer.contents.computer.length; i < numComputer; i++)
{
//If the testplace is in both objects then fire AJAX request
if (jenkinsComputer.contents.computer[i].displayName == key) //<<<This can happen only once per $.each loop, but it does not happen every time
{
//next $.each-iteration should only happen when received the JSON
var testplaceurl = jenkinsComputer.contents.computer[i].executors[0].currentExecutable.url;
$.when($.getJSON("php/ba-simple-proxy.php?url=" + encodeURI(testplaceurl) + "api/json?depth=1&pretty=1")).done(function(jenkinsUser)
{
//build object together...
});
}
}
}); //End of main Loop ($.each)
generateHTML(builtObject);
It would be great if someone could give me an advice how to do it.
I would do something like this:
var thingstodo = $(jsobject).length;
var notfired = true;
$.each(jsobject, function(key, value)
{
//build object together...
for (var i = 0, numComputer = jenkinsComputer.contents.computer.length; i < numComputer; i++)
{
//If the testplace is in both objects then fire AJAX request
if (jenkinsComputer.contents.computer[i].displayName == key) //<<<This can happen only once per $.each loop, but it does not happen every time
{
//next $.each-iteration should only happen when received the JSON
var testplaceurl = jenkinsComputer.contents.computer[i].executors[0].currentExecutable.url;
$.when($.getJSON("php/ba-simple-proxy.php?url=" + encodeURI(testplaceurl) + "api/json?depth=1&pretty=1")).done(function(jenkinsUser)
{
//build object together...
thingstodo--;
if(thingstodo === 0 && notfired){
notfired = false;
generateHTML(buildObject);
}
});
}else{
thingstodo--;
}
}
}); //End of main Loop ($.each)
if(thingstodo === 0 && notfired){
generateHTML(buildObject);
}
This is short untested example about the solution. I hope this to give you idea.
// I guess that jsobject is array ..
// if it is not object you can use something like:
// var keys = Object.getOwnPropertyNames(jsobject)
(function () {
var dfd = $.Deferred();
function is_not_finished() {
return jsobject.length > 0 && jenkinsComputer.contents.computer.length > 0;
}
(function _handleObject() {
var key = jsobject.shift();
var displayName = jenkinsComputer.contents.computer.shift().displayName;
if (displayName == key) //<<<This can happen only once per $.each loop, but it does not happen every time
{
//next $.each-iteration should only happen when received the JSON
var testplaceurl = jenkinsComputer.contents.computer[i].executors[0].currentExecutable.url;
$.getJSON("php/ba-simple-proxy.php?url=" + encodeURI(testplaceurl) + "api/json?depth=1&pretty=1").done(function(jenkinsUser)
{
//build object together...
if(is_not_finished()) {
setTimeout(_handleObject,0);
} else {
dfd.resolve();
}
});
} else if (is_not_finished()) {
setTimeout(_handleObject,0);
} else {
dfd.resolve();
}
}());
return dfd.promise();
}()).done(function () {
generateHTML(builtObject);
});

Can't update javaScript global variable

Here I have global variable userId, and i want to update it inside signInUserFunction(), to use is in other function. I have tried to define it using var, window, But all these didn't help. This variable doesn't update. As i see its about AJAX async. So, what can i do with it?
And yes, I know that its not good to make authentication with JS, I am quite new to it. So, I am just creating random methods to improve.
var userId = 1;
function signInUser() {
$.getJSON('http://localhost:8887/JAXRSService/webresources/generic/getAllUsers', function(data) {
var items = [];
var i = 0;
$.each(data, function(firstname, value) {
var str = JSON.stringify(value);
data = JSON.parse(str);
var innerId;
for (p in data) {
innerId = data[p].id;
if ($('#nameSignIn').val() == data[p].first_name && $('#passwordSignIn').val() == data[p].password) { //
userId = innerId;
window.location.href = "content.html";
break;
} else {
i++;
if (i == data.length) {
alert("Ощибка в логине или пароле!")
}
}
}
});
});
}
How are you determining whether or not it has been set? It looks like immediately after you set it, you navigate to a different page. When you get to that page, you will have an entirely new window.
Try alerting the value before navigating away.
EDITED: Here is how you could pass it to the other page (but you shouldn't do this in a real app)
window.userId=innerId;
alert(window.userId);
//this isn't a very secure way to do this. I DON'T recommend this
window.location.href = "content.html?id=" + innerId ;
Then in the other page, you could access it off the document.location:
alert(document.location.toString().split("?id=")[1]);
After reading my comments, you may want to try this:
var userId = 1;
function signInUser(){
$.getJSON('http://localhost:8887/JAXRSService/webresources/generic/getAllUsers', function(data){
var items = [], actors = data.Actors, l = 0;
$.each(actors, function(i, o){
l++;
if($('#nameSignIn').val() === o.first_name && $('#passwordSignIn').val() === o.password){
userId = o.id;
// this will redirect before any other code runs -> location = 'content.html';
if(l === actors.length){
alert('End of Loop');
}
}
});
});
}
signInUser();
I would not store sensitive data in JSON such as passwords. Use a database. There is no need to get all the data at the same time either.
Using the idea #mcgraphix proposed (and giving you the same warning...this would certainly not be the way to transfer data like this in a production environment), here is one way to do it:
function signInUser() {
var url = 'http://localhost:8887/JAXRSService/webresources/generic/getAllUsers';
var userId;
$.getJSON(url, function(data) {
$.each(data.Actors, function(index, actor) {
// Cache the values of the #nameSignIn and #passwordSignIn elements
var name = $('#nameSignIn').val();
var password = $('#passwordSignIn').val();
if (actor.first_name === name && actor.password === password) {
// We have found the correct actor.
// Extract its ID and assign it to userId.
userId = actor.id;
window.location.href = "content.html?userId=" + userId;
}
});
// This alert should only be reached if none of the actor objects
// has a name and password that matches your input box values.
alert("Ощибка в логине или пароле!");
});
}
// On the next page...
// Top answer from http://stackoverflow.com/questions/2090551/parse-query-string-in-javascript
// This approach can handle URLs with more than one query parameter,
// which you may potentially add in the future.
function getQueryVariable(variable) {
var query = window.location.search.substring(1);
var vars = query.split('&');
for (var i = 0; i < vars.length; i++) {
var pair = vars[i].split('=');
if (decodeURIComponent(pair[0]) == variable) {
return decodeURIComponent(pair[1]);
}
}
console.log('Query variable %s not found', variable);
}
var userId = getQueryVariable('userId');
Thanks you for help.Ended it all with usage of:
sessionStorage.getItem('label')
sessionStorage.setItem('label', 'value')

How to know all JSON object has been iterated? [duplicate]

This question already has answers here:
Wait until all jQuery Ajax requests are done?
(22 answers)
Closed 7 years ago.
I m working on phonegap product using jquery and jquery mobile, the scenario is, when user is logging in for first time, we sync all the data and after done we forward user to another view. The items are retrieved in json format from server. Here are the portion of my code. I have called the webservice and the response is returned as JSON objects in response variable.
$(response.data.module.registration).each(function(k,v){
//insert into app's sqlite database
});
$(response.data.module.attendance).each(function(k,v){
//insert into app's sqlite database
});
$(response.data.items.grocery).each(function(k,v){
//insert into app's sqlite database
});
//and so on. There could be many rows in each loop above.
My question is how to know all rows has been inserted from the loop so that I can forward user to restricted area.
more precisely, how to know all JSON object has been iterated successfully?
What i tried is put counter in each loop and check if sum of all the counters is equal to total items we are iterating. But this didn't work the sum of all the counters are readily available before all items are inserted.
EDIT
Here is my helper function that inserts record into sqlite db. This didn't work, user was logged in before all data are inserted. Can you tell me where I went wrong
var sqlhelper = {
insertJSONData:function(tablename,data,callback){
var dfrd = $.Deferred();
var fields=sqlhelper.separateFieldData(data,"field");
var dataval=sqlhelper.separateFieldData(data,"value");
sqlhelper.db.transaction(function(tx) {
var sqlquery='INSERT INTO '+tablename+' ('+fields+') values('+dataval+')';
console.log(sqlquery);
tx.executeSql(sqlquery,[],function(tx,result){
dfrd.resolve(result);
console.log('Success');
},sqlhelper.errorCB);
if(callback!=undefined){
callback();
}
});
return dfrd.promise();
}
}
And here is the code that fetches server response
function defObj1()
{
if(typeof response.data.module.registration!="undefined"){
$(response.data.module.registration).each(function(i,e){
var data = {
'reg_id': e.reg_id,
'reg_name': e.reg_name,
'reg_desc': e.reg_desc,
'reg_status': e.reg_status
};
sqlhelper.insertJSONData('tbl_registration',data);
}); // end of each loop
}
}
function defObj2()
{
if(typeof response.data.items.grocery!="undefined"){
$(response.data.items.grocery).each(function(i,e){
var data = {
'grocery_id': e.grocery_id,
'item_name': e.item_name,
'item_qty': e.item_qty,
'item_unit_price': e.item_unit_price
};
sqlhelper.insertJSONData('tbl_grocery',data);
}); // end of each loop
}
}
$.when(defObj1() ,defObj2()).done(function(a1,a2){
//sync complete so login user
doLogin();
})
Thanks
try this. (Edited)
var isValid = true, i = 0, sum, callback = function () {
//if all inserting is successfully it is called
};
...
$(response.data.module.registration).each(function (k, v) {
//insert into app's sqlite database
var data = {
'reg_id': e.reg_id,
'reg_name': e.reg_name,
'reg_desc': e.reg_desc,
'reg_status': e.reg_status
};
sqlhelper.insertJSONData('tbl_registration', data, function (data) {
if (!data) {
isValid = false;
sum++;
}
i++;//onSuccess function
checkLast(i);//call this lastly method or each
}, function () {
i++;//onError function
});
});
...
//other codes is identical logic
...
function checkLast(i) {
if (i == sum) {
callback();
}
}
...
I have added successCallbak and errorCallback to your sqlhelper
var sqlhelper = {
insertJSONData: function (tablename, data, successCallbak, errorCallback) {
var dfrd = $.Deferred();
var fields = sqlhelper.separateFieldData(data, "field");
var dataval = sqlhelper.separateFieldData(data, "value");
sqlhelper.db.transaction(function (tx) {
var sqlquery = 'INSERT INTO ' + tablename + ' (' + fields + ') values(' + dataval + ')';
console.log(sqlquery);
tx.executeSql(sqlquery, [], function (tx, result) {
dfrd.resolve(result);
if (successCallback) {
successCallback(result);
}
console.log('Success');
}, sqlhelper.errorCB);
if (errorCallback) {
errorCallback();
}
});
return dfrd.promise();
}
}

Variable scope or return issue (not sure which)

Using the script below I'm attempting to create an object called temptagarray which gets populated with all the tags on a Tumblr weblog and their frequency. So it should end up looking like this:
{'performance': 10, 'installation': 5}
I know the object is being created and it looks correct (I can print it out in each loop) but I can't figure out how to use it after/outside the function i.e. at the bottom of the script where I attempt to document.write() it out. Is this a global/local variable issue, a return issue or do I need to address it in some way?
<script type="text/javascript">
var temptagarray = {};
var tags;
var tag;
function loadPosts () {
var key = "api_key=9I4rZAYQCbU1o5TSMZuyrlvXiQsNxKBicCJxNK5OKZ6G9pgdim";
var api = "https://api.tumblr.com/v2/blog/garrettlynch.tumblr.com/";
var retrieve_more = function (offset) {
$.getJSON(api + "posts?callback=?&filter=image&limit=20&offset=" + offset + "&" + key,function(data) {
//for each item (post) in the response
$.each(data.response.posts, function(i, item) {
//pull out the posts tags
tags = item['tags'];
//loop through the tags
for (i = 0; i < tags.length; i++)
{
tag = tags[i];
//if the tag already exists in the tag array
if (temptagarray[tag])
{
temptagarray[tag] = temptagarray[tag] + 1;
}
else
{
temptagarray[tag] = 1;
}
}
});
if (data.response.posts.length == 20) {
retrieve_more(offset + 20);
}
});
};
retrieve_more(0);
}
loadPosts();
document.write(JSON.stringify(temptagarray));
</script>
Thanks in advance
Garrett
Replace this:
if (data.response.posts.length == 20) {
retrieve_more(offset + 20);
}
...with this:
if (data.response.posts.length == 20) {
retrieve_more(offset + 20);
} else {
document.write(JSON.stringify(temptagarray));
}
The problem you're having is that, despite your document.write(...) command being located below the ajax call in your code, the ajax call is asynchronous and thus the callback will be invoked asynchronously as well. Basically, document.write(...) is being invoked long before you've had a chance to interact with the temptagarray variable in the ajax callback.
First things first - AJAX is Async Asynchronous.
So the code block does not wait for the previous instruction to be completed before it executes the next line.
So your document.writeline would have already been executed by the time the response comes back.
Try printing that info in the success call back after the if block and you would indeed see the response.
thanks for the replies. Below is what I have now as a workable solution as the result is going to call another function anyway. Reading a little bit more I'm wondering if I should be using a callback - is it better?
<script type="text/javascript">
//load posts from a Tumblr weblog
function loadPosts () {
//api key and weblog address
var key = "api_key=9I4rZAYQCbU1o5TSMZuyrlvXiQsNxKBicCJxNK5OKZ6G9pgdim";
var api = "https://api.tumblr.com/v2/blog/garrettlynch.tumblr.com/";
//tags object
var temptagarray = {};
//all tags and each tag
var tags;
var tag;
//looping function to keep retrieving posts until all are retrieved
var retrieve_more = function (offset) {
$.getJSON(api + "posts?callback=?&filter=image&limit=20&offset=" + offset + "&" + key,function(data) {
//for each item (post) in the response
$.each(data.response.posts, function(i, item) {
//pull out the posts tags
tags = item['tags'];
//loop through the tags
for (i = 0; i < tags.length; i++)
{
//pull out each tag
tag = tags[i];
//if the tag already exists in the tag array
if (temptagarray[tag])
{
//add 1 to its count
temptagarray[tag] = temptagarray[tag] + 1;
}
else
{
//set its count to 1
temptagarray[tag] = 1;
}
}
//to test object as it gets added to
//$("#Posts ul").append('<li>' + JSON.stringify(item, ['tags']) + '</li>')
});
//if the number of posts is more than 20
if (data.response.posts.length == 20)
{
//retrieve the next 20
retrieve_more(offset + 20);
}
else
{
//call the show result function
showresult(temptagarray);
}
});
};
//stop retrieving posts
retrieve_more(0);
}
loadPosts();
function showresult(tagarray)
{
$("#Posts ul").append('<li>' + JSON.stringify(tagarray) + '</li>');
//document.write(JSON.stringify(tagarray));
}
</script>

AngularJS looping through http get req to find correct url

So I'm currently working on a project where I'm making a http request with angular to around 1500 URLs looking for the json that matches with the condition I have (only 1 of the URLs will match). I currently have an implementation that sometimes work (but isnt deterministic I'm assuming because it the requests are asynchronous although it might just be a bug??). I'm still kinda new to angular so I'm not sure if I'm doing it correctly at all so I'm open to changing the code entirely!
this.matchingurl;
this.data;
this.findUrl = function(condition) {
var that = this;
for (var i = 0; i <= ; i++) {
// this is just looping through the url list
for (var i = 0; i < urlList.length; i++) {
for (var j = 0; j < urlList[i]['list'].length; j++) {
this.url = 'http://' + urlList[i]['list'][j] + restofurl;
var tempUrl = urlList[i]['list'][j];
$http.get(this.url).success(function(data) {
if (condition is met in data) {
that.matchingurl = tempUrl;
return;
}
})
.error(function(data){
// error handling
});
}
}
}
}
TLDR: matchingUrl isn't what I expect? Still goes inside the "condition" loop but doesn't spit out right url. Always gives me the same "url" for any sublist, right or wrong.
I would suggest that you use $q promise of angularjs to do the task, either you can check one url at a time serially( slow if you ask me), or get all results at a time by requesting parallely. Below, I have done a crude implementation of the latter
this.findUrl = function(condition) {
var urls =[], self = this, oUrl; // collect all the urls
urlList.forEach(function(list){
list.forEach(function(url){
oUrl.push(url);
urls.push('http://' + url + restofurl); // not sure where you are getting this restofurl from...
});
});
$q.all(urls.map(function(url){
return $http.get(url); // returns promise for each url, thus mapping all urls to promise.
})).then(function(datas){
datas.some(function(data, i){
if(data == condition){ // change as per requirement
self.matchingurl = oUrl[i];
return true;
}
})
});
}
Edit:
same thing done checking one url at a time:
this.findUrl = function(condition) {
var urls =[], self = this, oUrl; // collect all the urls
urlList.forEach(function(list){
list.forEach(function(url){
oUrl.push(url);
urls.push('http://' + url + restofurl); // not sure where you are getting this restofurl from...
});
});
function check(i){
function fail(){ // move to check the next url in the array
i++;
if(i<urls.length) return check(i);
console.log('none of the urls are matching');
}
return http.get(urls[i]).then(function(data){
if(data == condition){ // change as per requirement
self.matchingurl = oUrl[i];
}else{
fail();
}
}).catch(fail);
}
check(0); // start the chain
}
You are right, this you could run into trouble due to the synchronous http calls if you don't handle your variables correctly. Here is a snippet to achieve the same using synchronous http calls.
this.matchingurl;
this.data;
this.findUrl = function(condition, i, j) {
var that = this;
this.url = 'http://' + urlList[i]['list'][j] + restofurl;
var tempUrl = urlList[i]['list'][j];
$http.get(this.url).success(function(data) {
if (condition is met in data) {
that.matchingurl = tempUrl;
return;
}
else{
if(urlList[i]['list'].length > j + 1){
j++;
}
else{
if(urlList.length > i+1){
i++;
j=0;
}
else{
return;
}
}
this.findUrl(condition, i, j);
}
})
.error(function(data){
// error handling
});
}
}
}
}
this.findUrl(condition, 0, 0);

Categories

Resources