How to get the right index for a delayed trigger? - javascript

Following problem: I have an angular module with $http.get to get some google coordinates. This function triggers another function. This function again triggers another function. It will all make sense in a moment.
Angular Module:
var myApp = angular.module('wmw', []);
myApp.controller('MainCtrl', function ($scope, $http) {
//Angular Method
$scope.getTargetCords = function (data) {
$http.get(data).success(function (response) {
$(document).triggerHandler('my_func:data-received', [response]);
});
};
});
onSucess:
var onSuccess = function(position) {
currentLat = position.coords.latitude ;
currentLng = position.coords.longitude;
for(i = 0; i<locations.length;i++){
var destUrl = 'http://maps.googleapis.com/maps/api/geocode/xml?address=' + locations[i][ 'street' ] + ',' + locations[i][ 'city' ] + ',Deutschland' + '&sensor=true';
var MyAngularScope = angular.element($("#MainCtrlId")).scope();
MyAngularScope.getTargetCords('http://maps.googleapis.com/maps/api/geocode/xml?address=' + locations[i][ 'street' ] + ',' + locations[i][ 'city' ] + ',Deutschland' + '&sensor=true');
}
};
navigator.geolocation.getCurrentPosition(onSuccess, onError);
The two triggers:
$(document).on('my_func:data-received', function(event, response) {
map[s] = response;
s++;
if(s === locations.length){
$(document).triggerHandler('allData');
}
});
$(document).on('allData', function(){
var thecoords = [];
var distance = [];
$('#filter-list').empty();
for(var i = 0; i < locations.length; i++){
thecoords[0] = $(map[i]).find('lat').first().text();
thecoords[1] = $(map[i]).find('lng').first().text();
distance[i] = calculateDistance(currentLat, currentLng, thecoords[0], thecoords[1]);
locations[i]['distance'] = distance[i];
}
locations.sort(function(a,b)
{ return a.distance - b.distance;}
);
for(var i = 0;i < locations.length; i++){
distance[i] = locations[i]['distance'].toFixed(2);
distance[i] += ' KM';
locations[i]['distance'] = distance[i];
}
$('.loading').hide();
for(var i = 0; i<=5; i++){
addItemToList(locations[i]);
}
});
What's happening? With those functions I retrieve the current location, the dest location and calculate the difference in KM via a lat./long. calc function which I found on the web. .loading is just a div with "Calculating route..." and a transparent grey background. So once everything is finished, The "Distance" of every route will change to the calculated distance.
The problem with this: in my ".on('my_func:data-received')" I am using the variable "s" which is 0 at the start. In my logic I thought, that this would then put the responses one after another in my "map". But now I realised, that the "data-received" are not called one after another, but each time when data is retrieved. So when locations[0] is calling the $http.get and then after this locations[1] is calling the $http.get, it could happen, that locations[1] retrieves the data earlier. How could I have my "s" always be the right number? So that when I have locations[1] calling $http.get map[1] will be locations[1] response?
My head is exploding, as I cant find a solution to this problem, although it seems to be so basic.
Thank you in advance!

Since restructuring your application is not an option, another reasonably quick way of getting the right order is mapping the response data to the original array. The response contains the url which is built using data from the array which might give you what you need.

Related

How to update a global variable inside a nested function in angularjs?

This is the code I am using. currentId is 10; I am making a service call which has a function of $http.get(to a JSON) and I want to attack the array in JSON's length to currentId after the function is executed. How do I do it? Is there any special function in angularjs which helps me. I have read the other relevant questions here, but I need this done in angularjs. Thanks.
var currentId = 10;
console.log(currentId + ' before function'); //outputs 10
function findId(){
readJson.readJsonfun().then(function(data) {
currentId = data.length; //say data.length = 20;
return currentId;
});}
findId();
console.log(currentId + ' before function'); //should output 20?
As it's Async function - use async approach
var currentId = 10;
var modifiedId1;
var modifiedId2;
console.log(currentId + ' before function call');
function findId(){
return readJson.readJsonfun().then(function(data) {
currentId = data.length;
return $q.when(currentId);
});
}
findId().then(function(id){
//first time we need it
firstFunction(currentId); //or firstFunction(id);
});
function firstFunction(id){
modifiedId1 = id * 2;
}
function secondFunction(){
return findId().then(function(id){
//second time we need it, currentId is updates
modifiedId2 = id * 4;
return $q.when();
});
}
Store the variable of concern (currentID) in the service. Inside your service, it would look something like this:
.service('Example', function(){
var service = this;
var currentID = null;
service.getCurrentID = function() {
return currentID;
}
service.findId(){
readJson.readJsonfun().then(function(data) {
currentId = data.length; //say data.length = 20;
});}
Then, from outside of the service, you could call Example.findID() to update the currentID, and then Example.getCurrentID() to retrieve the actual value

Callback is undefined and other stories

I got the following script, which is not working propperly. I know about getJSON's async nature, so I tried to build a callback function (jsonConsoleLog), which is supposed to be executed before getJSON get asigned to var (myJson = json;). After running debug in Chrome, I got two things out: A) debug is highlighting jsonConsoleLogcalls inside getJSON function as undefined.
B) Console is throwing TypeError: Cannot read property '0' of null for var friends = myJSON[0].friends;, which means the whole function doesn't work.
I'm in battle with it since saturday and I really don't know what to do. There's clearly something up with my callback function, but shoot me if I know what. Help?
var myJSON = null;
var main = document.getElementsByClassName('main');
var sec = document.getElementsByClassName('sec');
function getJSON(jsonConsoleLog){
$.getJSON('http://www.json-generator.com/api/json/get/cpldILZRfm? indent=2', function(json){
if (json != null){
console.log('Load Successfull!');
};
if (jsonConsoleLog){
jsonConsoleLog(json[0].friends);
}
myJSON = json;
});
};
function jsonConsoleLog(json) {
for (var i = 0; i < json.length; i++) {
console.log('friend: ' + friends[i]);
};
};
getJSON();
var friends = myJSON[0].friends;
function myFn1(){
for(var i = 0; i < friends.length; i++) {
main[i].innerHTML = friends[i].id;
};
};
function myFn2(){
for(var i = 0; i < friends.length; i++) {
main_div[i].innerHTML = friends[i].name;
};
};
main.innerHTML = myFn1();
sec.innerHTML = myFn2();
The first problem is because your function getJSON is expecting one formal argument, which you've called jsonConsoleLog. But you are not passing any arguments to getJSON. This means that inside getJSON the formal parameter, jsonConsoleLog, will indeed be undefined. Note that because you've named the formal parameter jsonConsoleLog, which is the same name as the function you're hoping to call, inside getJSON you won't have access to the function. What you need to do is pass the function as the parameter:
getJSON(jsonConsoleLog);
The second problem is I think to do with the json variable - it doesn't have a property 0 (i.e. the error is occurring when you try to treat it as an array and access element 0), which suggets that json is coming back empty, or is not an array.
you're calling getJSON without the callback parameter - therefore, the local variable jsonConsoleLog is undefined in getJSON
snip ...
function blah(json) { // changed name to avoid confusion in the answer - you can keep the name you had
for (var i = 0; i < json.length; i++) {
console.log('friend: ' + friends[i]);
};
};
getJSON(blah); // change made here (used the function name blah as changed above
var friends = myJSON[0].friends;
function myFn1(){
for(var i = 0; i < friends.length; i++) {
main[i].innerHTML = friends[i].id;
};
};
snip...
The issue with
var friends = myJSON[0].friends;
is duplicated here many many times ... $.getJSON is asynchronous and you are trying to use it synchronously
i.e. when you assign var friends = myJSON[0].friends; myJson hasn't been assigned in $.getjson ... in fact, $.getjson hasn't even BEGUN to run
here's all your code reorganised and rewritten to hopefully work
var main = document.getElementsByClassName('main');
var sec = document.getElementsByClassName('sec');
function getJSON(callback) {
$.getJSON('http://www.json-generator.com/api/json/get/cpldILZRfm? indent=2', function(json) {
if (json != null) {
console.log('Load Successfull!');
};
if (callback) {
callback(json);
}
});
};
function doThings(json) {
var friends = json[0].friends;
for (var i = 0; i < friends.length; i++) {
console.log('friend: ' + friends[i]);
};
function myFn1() {
for (var i = 0; i < friends.length; i++) {
main[i].innerHTML = friends[i].id;
};
};
function myFn2() {
for (var i = 0; i < friends.length; i++) {
main_div[i].innerHTML = friends[i].name;
};
};
main.innerHTML = myFn1();
sec.innerHTML = myFn2();
}
getJSON(doThings);
Correct, fully working code (basically the same as accepted, correct answer but stylistycally bit different)
var main = document.getElementsByClassName('main');
var sec = document.getElementsByClassName('sec');
var friends = null;
function getJSON(jsonConsoleLog){
$.getJSON('http://www.json-generator.com/api/json/get/cpldILZRfm?indent=2', function(json){
if (json != null){
console.log('Load Successfull!');
};
if (jsonConsoleLog){
jsonConsoleLog(json[0].friends);
}
});
};
function jsonConsoleLog(json) {
for (var i = 0; i < json.length; i++) {
console.log('friend: ' + json[i]);
};
friends = json;
myFn1();
myFn2();
};
function myFn1(){
for(var i = 0; i < friends.length; i++) {
main[i].innerHTML = friends[i].id;
};
};
function myFn2(){
for(var i = 0; i < friends.length; i++) {
main[i].innerHTML += friends[i].name;
};
};
getJSON(jsonConsoleLog);

Variable from for loop always returns 0

I am reasonably new to node.js / sails.js and have run into a problem that I know the answer is simple but I cannot seem to work it out.
My code is as follows
SendCompleted : function(req,res)
{
var updated = 0;
var params = req.params.all();
var dt = JSON.parse(decodeURI(params.id));
var connection = new sql.Connection(testrmis, function (err)
{
if (err) {
}
for(var i = 0; i < dt.length; i++) {
var obj = dt[i];
var request = new sql.Request(connection);
request.stream = true;
request.input('branchid', sql.Int(), obj.branch_id);
request.input('picklistid', sql.Int(), obj.picklist_id);
request.input('scanned',sql.Int(),obj.scanned);
request.input('expected', sql.Int(),obj.expected);
request.input('poscode', sql.VarChar(),obj.poscode);
request.input('label', sql.VarChar(), obj.label);
request.input('dt', sql.VarChar(), obj.dt);
request.execute('WAREHOUSE_InsertPiPackData');
request.on('done', function(returnValue) {
updated = updated + returnValue;
console.log(updated);
});
}
res.send("[{\"ReturnValue\":" + updated + "}]");
});
}
I am sending in 4 lines of results and my console.log(updated) counts up as it should for each line, e.g 1,2,3,4
However the res.send result for updated is always 0.
Could anyone please explain why this is happening? My var updated is outside of my loop and this is getting updated correctly, however when the loop is finished it seems to get reset to 0?
returnValue == ##rowcount from the stored procedure
request is async so
res.send("[{\"ReturnValue\":" + updated + "}]");
gets executed even before you get the callback on request as JS doesn't wait for the callback and executes the next line. What you can do is use a counter and place your res.send inside for loop.
SendCompleted : function(req,res)
{
var updated = 0;
var params = req.params.all();
var dt = JSON.parse(decodeURI(params.id));
var connection = new sql.Connection(testrmis, function (err)
{
if (err) {
}
var count = dt.length;
for(var i = 0; i < dt.length; i++) {
var obj = dt[i];
var request = new sql.Request(connection);
request.stream = true;
request.input('branchid', sql.Int(), obj.branch_id);
request.input('picklistid', sql.Int(), obj.picklist_id);
request.input('scanned',sql.Int(),obj.scanned);
request.input('expected', sql.Int(),obj.expected);
request.input('poscode', sql.VarChar(),obj.poscode);
request.input('label', sql.VarChar(), obj.label);
request.input('dt', sql.VarChar(), obj.dt);
request.execute('WAREHOUSE_InsertPiPackData');
request.on('done', function(returnValue) {
count--;
updated = updated + returnValue;
console.log(updated);
if(count == 0) res.send("[{\"ReturnValue\":" + updated + "}]");
});
}
});
}
Try for this:
May be Async problem:
for(var i = 0; i < dt.length; i++) {
//Your logic
if(i=== dt.length){
res.send("[{\"ReturnValue\":" + updated + "}]");
}
}
This is because at the time you do request.send, the value of updated is not incremented. This is because request.execute is asynchronous and done handler will be invoked after the res.send has been executed.
I would recommend a promise library (example, q). You can combine the promises and then use Q.all to do req.send when all the promises are done.
See more details here

Asynchronous Problems with Javascript Hacker News API

Hacker News recently released an API that I am using to display what the current top ten items are on Hacker News. I am running into some problems.
When I run the code below, the order of the items on the frontpage are inaccurate, jumping from the second one in the frontpage to the fourth, to the first, to the fifth, to the third and so on. Running the code again results in a slightly different order again.
$.getJSON('https://hacker-news.firebaseio.com/v0/topstories.json', function(json) {
var convoText = '<ol>';
for (var i = 0; i < 10; i++) {
(function(i) {
$.getJSON('https://hacker-news.firebaseio.com/v0/item/' + json[i] + '.json', function(json2) {
convoText += '<li>' + json2.title + '</li>';
if (i === 9) {
convoText += '</ol>';
addConvo(convoText);
}
});
})(i);
}
});
I understand that this is an effect of Javascript's asynchronous nature. How can I fix it?
The knack is to create and append a <li><a></a></li> structure synchronously in the loop - thereby establishing the correct order - then populate it asynchronously with json2 data when it arrives.
$.getJSON('https://hacker-news.firebaseio.com/v0/topstories.json', function(json) {
var $ol = $('<ol/>').appendTo(...);//wherever
for (var i = 0; i < Math.min(json.length, 10); i++) {
(function(i) {
var $a = $('<li><a></a></li>').appendTo($ol).find('a');
$.getJSON('https://hacker-news.firebaseio.com/v0/item/' + json[i] + '.json', function(json2) {
$a.attr('href', json2.url).text(json2.title);
});
})(i);
}
});
You will have to complete the .appendTo(...) line. I don't know from the question where the <ol>...</ol> is appended.
You can use jQueries $.when for that:
$.getJSON('https://hacker-news.firebaseio.com/v0/topstories.json', function(json) {
var requests = [];
for (var i = 0; i < 10; i++) {
requests.push($.getJSON('https://hacker-news.firebaseio.com/v0/item/' + json[i] + '.json'));
}
$.when.apply($, requests).done(function() {
var results = [].slice.call(arguments);
var list = results.map(function(arr) {
return '<li>' + arr[0].title + '</li>';
});
var convoText = '<ol>' + list.join('') + '</ol>';
console.log(convoText);
});
});
There are a few ways to fix this. The easiest is, instead of appending to convoText, use an array, and set its index when you get data. Like data[i] = json2;. Then when all your data is fetched, join your array.
A more structural fix would be to rearchitect your loop as a collection of promises, and construct your HTML when they have all resolved (what #xat was alluding to above).

Asynchronous Function in Iteration - javascript

I am trying not to replicate code and loop over a a function in d3 that is asynchronous. Here is some code
Since d3.text is asynchronous , I am not able to use the index u in a correct way to append objects to the DOM. How should I go about this? I need the loop to go to next iteration once d3.text finished
for(var u in urls) {
console.log(u);
var url = "interest_points/" + urls[u] + ".csv";
var data_gpBy_month = {};
var sortable_month = []
d3.text(url, function(text) {
// some code...
var data = d3.csv.parseRows(text).map(function(row) {
//some code...
});
//some code
});
}
Something like this (fiddle: http://jsfiddle.net/EYAYT/2/) ?
var urls = ["asd", "asdasd", "Asdasfa"];
var currentUrlIndex = 0;
var getUrl = function(){
if (currentUrlIndex >= urls.length){
return null;
} else {
return "interest_points/" + urls[currentUrlIndex++] + ".csv";
}
}
var execd3Text = function(){
var url = getUrl();
if (url){
d3.text(url, function(text) {
//some code;;
execd3Text();
});
}
}
execd3Text();
The loop should simply become this:
for(var u in urls) { loadParseAndRender(u); }
All your existing logic then moves into loadParseAndRender, but at this point u will never get overridden. I.e, in fancy terms, it gets captured in the closure.
function loadParseAndRender(u) {
// the rest of your code
}
What David W suggested is the same thing as abive, but without creating a named function for it, you'd do this:
for(var _u in urls) {
(function(u) { // this is an anonymous function
// the rest of you code
})(_u) // this function gets called as soon as it's declared
}
If I understood properly:
function doSomething(array) {
var u = array.shift();
console.log(u);
var url = "interest_points/" + urls[u] + ".csv";
var data_gpBy_month = {};
var sortable_month = []
d3.text(url, function(text) {
// some code...
var data = d3.csv.parseRows(text).map(function(row) {
//some code...
});
//some code
if (array.length > 0)
doSomething(array);
});
doSomething(Object.keys(urls));

Categories