Getting undefined because of asynchronous calling - node.js

I'm having a bit of a problem here. I'm working on a backend on NodeJS for a Sencha Touch app and at some point I'm calling a function, but I'm getting to next line of code before getting that functions return statement.
Here's my code...
.... for loop ....
if(resultSet[k].id_bus == busDocs[n].id_bus && resultSet[k].id_bus_variation == busDocs[n].id_bus_variation){
resultSet[k].s_origin_description = busDocs[n].s_origin_description;
resultSet[k].s_eta = vehicle != null ? getVehicleETA(db, vehicle) : 'Unknown';
console.log('after getting eta');
resultSet[k].s_destination_description = busDocs[n].s_destination_description;
}
}
}
res.send(JSON.stringify(resultSet));
....
and this is my getVehicleETA function...
getVehicleETA = function(db, vehicle){
var position = vehicle.position;
function compare(a,b) {
if (a.n_stop_number < b.n_stop_number)
return -1;
if (a.n_stop_number > b.n_stop_number)
return 1;
return 0;
}
db.get('busstops').find({$query:{$and:[{id_bus: vehicle.id_bus}, {id_bus_variation: vehicle.id_bus_variation}]},$orderBy:{n_stop_number: 1}},function(e, docs){
var distance = 0;
if(docs.length != 0){
docs.sort(compare);
var nextBusStop = null;
for(var i=0; i<docs.length; i++){
if(i+1 < docs.length){
var routeSegmentLength = Math.sqrt(Math.pow(docs[i +1].coord_x - docs[i].coord_x, 2) + Math.pow(docs[i +1].coord_y - docs[i].coord_y, 2));
var firstStopDistance = Math.sqrt(Math.pow(vehicle.coord_x - docs[i].coord_x, 2) + Math.pow(vehicle.coord_y - docs[i].coord_y, 2));
var secondStopDistance = Math.sqrt(Math.pow(vehicle.coord_x - docs[i +1].coord_x, 2) + Math.pow(vehicle.coord_y - docs[i +1].coord_y, 2));
if(nextBusStop != null){
distance += routeSegmentLength;
}
if(secondStopDistance < routeSegmentLength && firstStopDistance < routeSegmentLength){
nextBusStop = docs[i+1];
}
}
}
console.log(((distance/(1000 * vehicle.speed)) * 60))
return ((distance/(1000 * vehicle.speed)) * 60);
}
});
}
If this was working correctly I would have to see getVehicleETA's console.log first, and then console.log('after getting eta'); but I'm getting it the other way around. I know this is actually correct behaviour as it didn't block the thread and kept going on in the code, but this is not working for me, because I'm sending the resultSet before even getting my getVehicleETA result, and I need resultSet's items to have it's s_eta property set before sending them.
What's the correct way of doing this?

Correct way is to have a callback in getVehicleETA function.
Let me show you as an illustration:
getVehicleETA = function(db,vehicle,callback){
/*do some stuff*/
//return the result in callback.
callback(result);
}
resultSet[k].s_origin_description = busDocs[n].s_origin_description;
if(vehicle!=null){
getVehicleETA(db,vehicle,function(result){
resultSet[k].s_eta = result;
nextAction()
});
}else{
resultSet[k].s_eta = "unknown";
nextAction();
}
function nextAction (){
console.log('after getting eta');
resultSet[k].s_destination_description = busDocs[n].s_destination_de
res.send(JSON.stringify(resultSet));
}
Hope this works for you.

You're calling an asynchronous function as if it were synchronous. Also, returning a value from a callback for an asynchronous function is meaningless. You have to pass in a callback to the function and then call that callback within your database callback with the values from the database.
You should look into using the async module to help you handle asynchronous function calls in a loop.

Related

Need to execute function when forEach functions ends

I have this code in node js / firebase :
ref.child("recipts").once("value", function(usersSnap) {
usersSnap.forEach(function(reciptsSnap) {
reciptsSnap.forEach(function(reciptSnap) {
reciptSnap.ref.child("last_recipt").once("value", function(b) {
b.forEach(function(c) { //Here I fill some "product" object
});
});
reciptSnap.forEach(function(b) { //Here I fill some "product" object
});
});
});
});
I need to execute a function just when "reciptSnap" forEachs finished. How can I accomplish this, I try using a variable i++ and i-- but only work for one forEach iteration.
The function I call is for manipulating the product object I created with the filled data from the forEachs loops.
If I have understood correctly, you want to call a function when reciptsSnap.forEach is complete and all async tasks inside it are also complete.
For achieving this, you can use the index parameter and the original array that is passed to the callback function of forEach. (See Documentation)
The code will be like this:
(Note: The following code is without changing the current forEach loop structure used. However, re-writing the code with Promise or async would be a better & cleaner way to do it).
var loop1Done = false;
var loop2Done = false;
ref.child("recipts").once("value", function (usersSnap) {
usersSnap.forEach(function (reciptsSnap) {
reciptsSnap.forEach(function (reciptSnap, index, colA) {
const idx = index;
const col = colA;
reciptSnap.ref.child("last_recipt").once("value", function (b) {
const i = idx;
const c = col;
b.forEach(function (c, j, colB) { //Here I fill some "product" object
// Do what you want here
// Check if all done for this loop
if ((j >= colB.length) && (i >= c.length)) {
loop1Done = true;
// Check if all loops done
if (loop1Done && loop2Done) {
// Call final callback function
// e.g. myFinalCallback();
}
}
});
});
reciptSnap.forEach(function (b, k, colC) { //Here I fill some "product" object
const i = idx;
const c = col;
// Do what you want here
// Check if all done for this loop
if ((k >= colC.length) && (i >= c.length)) {
loop2Done = true;
// Check if all loops done
if (loop1Done && loop2Done) {
// Call final callback function
// e.g. myFinalCallback();
}
}
});
});
});
});
Try:
reciptSnap.child("last_recipt").forEach(function(b) {
b.forEach(function(c) {
//Here I fill some "product" object
});
});
This should work since all of your data should already have been fetched when you did "value" on the receipts node.
If this works, your code is no longer asynchronous and right after the last forEach, you can execute the function you wanted to.
reciptSnap.forEach(function(b) {
//Here I fill some "product" object
});
//Execute your function here
});

How to cache outer scope values to be used inside of Async NodeJS calls?

Something like the below code illustrates my intention, if you can imagine how a naive programmer would probably try to write this the first time:
function (redisUpdatesHash) {
var redisKeys = Object.keys(redisUpdatesHash);
for (var i = 0; i < redisKeys.length; i++) {
var key = redisKeys[i];
redisClient.get(key, function (err, value) {
if (value != redisUpdatesHash[key]) {
redisClient.set(key, redisUpdatesHash[key]);
redisClient.publish(key + "/notifications", redisUpdatesHash[key]);
}
});
}
}
The problem is, predictably, key is the wrong value in the callback scopes of the asynchronous nature of the node_redis callbacks. The method of detection is really primitive because of security restrictions out of my control - so the only option for me was to resort to polling the source for it's state. So the intention above is to store that state in Redis so that I can compare during the next poll to determine if it changed. If it has, I publish an event and store off the new value to update the comparison value for the next polling cycle.
It appears that there's no good way to do this in NodeJS... I'm open to any suggestions - whether it's fixing the above code to somehow be able to perform this check, or to suggest a different method of doing this entirely.
I solved this problem through using function currying to cache the outer values in a closure.
In vanilla Javascript/NodeJS
asyncCallback = function (newValue, redisKey, redisValue) {
if (newValue != redisValue) {
redisClient.set(redisKey, newValue, handleRedisError);
redisClient.publish(redisKey + '/notifier', newValue, handleRedisError);
}
};
curriedAsyncCallback = function (newValue) {
return function (redisKey) {
return function (redisValue) {
asyncCallback(newValue, redisKey, redisValue);
};
};
};
var newResults = getNewResults(),
redisKeys = Object.keys(newResults);
for (var i = 0; i < redisKeys.length; i++) {
redisClient.get(redisKeys[i], curriedAsyncCallback(newResults[redisKeys[i]])(redisKeys[i]));
}
However, I ended up using HighlandJS to help with the currying and iteration.
var _ = require('highland'),
//...
asyncCallback = function (newValue, redisKey, redisValue) {
if (newValue != redisValue) {
redisClient.set(redisKey, newValue, handleRedisError);
redisClient.publish(redisKey + '/notifier', newValue, handleRedisError);
}
};
var newResults = getNewResults(),
redisKeys = Object.keys(newResults),
curriedAsyncCallback = _.curry(asyncCallback),
redisGet = _(redisClient.get.bind(redisClient));
redisKeys.each(function (key) {
redisGet(key).each(curriedAsyncCallback(newResults[key], key));
});

turning a synchronous function into asynchronous function

I have a for loop which does many iterations .I would like to put that piece of code in a custom async function as it is blocking.Is there anyway I can write a function so it will call a callback once the loop iteration is over?.
Use asynchronous-function-inside-a-loop paradigm. This ensures that the asynchronous functions get called with the correct value of the index variable.
var total = someObject.list.length;
var count = 0;
for(var i = 0; i < total; i++){
(function(foo){
myobj.get(someObject.list[foo], function(err, response) {
do_something(foo);
count++;
if (count > total - 1) done();
});
}(i)); //To immediately invoke the function passing 'i' as parameter
}
function done() {
console.log('All data loaded');
}

Error on callback when running a recursion function using async

I try to execute a recursion through a tree, in order to exec node_func for each node in the tree. node_func also returns the next values in the tree under [values].
I use async.eachSeries which get a list of the nodes in the next level of the tree.
The function runs successfully over the first branch of the tree, but at the leaf where I have the stop condition, I try to call the callback but it's undefined.
The code:
function clone(a) {
return JSON.parse(JSON.stringify(a));
}
var searchNext = function(params, callbackSN){
var seParams = clone(params);
node_func(seParams,function(searchRes){
//Stop Condition - return
if (searchRes["nextFeature"] != 1){
return callbackSN(); //Stop Condition
}
var values = searchRes["values"]
var paramsArr = []
for (var i = 0; i < values.length; i++) {
var seParams2 = clone(seParams);
seParams2["value"].push(values[i]["value"])
paramsArr.push(seParams2)
};
async.eachSeries(paramsArr, searchNext, function(err){
return callbackSN(err)
});
})
}
//init search
var params = {"value" :[]}
searchNext(params,console.log)
When I run it, it runs over the first branch, and when it gets to the "Stop Condition" I get the following error:
TypeError: undefined is not a function
Pointing to the line:
return callbackSN(); //Stop Condition
At the Stop Condition
In the line
return callback(err)
You are invoking the callback function, but it is not defined in your code. I guess you want to call the callbackSN function.
return callbackSN(err)
I had an error in the original code :
function clone(a) {
return JSON.parse(JSON.stringify(a));
}
var searchNext = function(params,db, callbackSN){
var seParams = clone(params);
node_func(seParams,db,function(searchRes){
//Stop Condition - return
if (searchRes["nextFeature"] != 1){
return callbackSN(); //Stop Condition
}
var values = searchRes["values"]
var paramsArr = []
for (var i = 0; i < values.length; i++) {
var seParams2 = clone(seParams);
seParams2["value"].push(values[i]["value"])
paramsArr.push(seParams2)
};
async.eachSeries(paramsArr, searchNext, function(err){
return callbackSN(err)
});
})
}
//init search
var params = {"value" :[]}
searchNext(params,console.log)
The second variable "db" at node_func cannot be called from the async, so it made the confusion.
I added the "db" variable at the parent function as a local variable.

Good way to handle Q Promises in Waterline with Sails.js

I have a problem that I'm importing some data and each new row depends on previous row being added (since each row has its order attribute set based on current maximum order from other objects). The flow is that I first try to find object with the same name, if not found I first check maximum order and create new object with order + 1 from that query.
I tried doing this with Q promises which are available under Waterline. I tried using all method as well as combining queries with then from Q docs:
var result = Q(initialVal);
funcs.forEach(function (f) {
result = result.then(f);
});
return result;
But all objects had the same order, just like they would be executed in parallel, instead of waiting for the first chain to finish.
I finally found a solution with recurrency, but I doubt it's the best way of working with promises. Here's the code that works (+ needs some refactor and cleaning etc.), to show the rough idea:
function findOrCreateGroup(groupsQuery, index, callback) {
var groupName = groupsQuery[index];
Group.findOne({ 'name' : groupName }).then(function(group) {
if (!group) {
return Group.find().limit(1).sort('order DESC').then(function(foundGroups) {
var maxOrder = 0;
if (foundGroups.length > 0) {
maxOrder = foundGroups[0].order;
}
return Group.create({
'name' : groupName,
'order' : (maxOrder + 1)
}).then(function(g) {
dbGroups[g.name] = g;
if (index + 1 < groupsQuery.length) {
findOrCreateGroup(groupsQuery, index + 1, callback);
} else {
callback();
}
return g;
});
});
} else {
dbGroups[group.name] = group;
if (index + 1 < groupsQuery.length) {
findOrCreateGroup(groupsQuery, index + 1, callback);
} else {
callback();
}
return group;
}
});
}

Resources