Error on callback when running a recursion function using async - node.js

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.

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));
});

Getting undefined because of asynchronous calling

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.

node.js - why this local function can modify global variables?

Here is my code:
var handleCondition = function(condition,params){
var dup_condition;
dup_condition = condition;
var isArray = function(obj) {
return Object.prototype.toString.call(obj) === '[object Array]';
};
var __replace = function(str){
var reg_slot = /^#(.+)/;
if(reg_slot.test(str) == true){
var ss = reg_slot.exec(str)[1];
return params[ss];
}else{
return str;
}
};
var compare = function(a){
var arr = a;
if(params != undefined){
for(var j =1;j<arr.length;j++){
arr[j] = __replace(arr[j]);
}
}
switch(arr[0]){
case "$eq":
case "==":
return (arr[1] == arr[2]);
default:
return (arr[1] == arr[2]);
}
};
if(isArray(dup_condition)){
var im = function (arr){
for(var i=0;i<3;i++){
if(isArray(arr[i])){
arr[i] = im(arr[i]);
}
}
return compare(arr);
};
var res = im(dup_condition);
return res;
}
};
/*Here are test data*/
var c = {
"beforeDNS":
["$eq","#host",["$eq",10,10]]
,
"afterDNS":["$match",/^10\.+/,"#ip"]
};
var params ={
host:"dd"
};
console.log(c["beforeDNS"]); // ==> ["$eq","#host",["$eq",10,10]]
handleCondition(c["beforeDNS"],params);
console.log(c["beforeDNS"]); // ==> ["$eq","dd",true]
handleCondition(c["beforeDNS"],params);
The first time I run the code with the expected result;
However , when I tried to run the function second time,to my surprise,the value of c["beforeDNS"] has changed unexpectedly!
In fact,I haven't write any code in my function to modify the value of this global variable,but it just changed.
So please help me find the reason of this mysterious result or just fix it.Thanks!
Your dup_condition variable isn't duping anything. It's just a reference to the argument you pass in.
Thus when you pass it to the im function, which modifies its argument in place, it is just referencing and modifying condition (which is itself a reference to the c["beforeDNS"] defined outside the function).
To fix this you might use slice or some more sophisticated method to actually dupe the arguments. slice, for example, would return a new array. Note though that this is only a shallow copy. References within that array would still refer to the same objects.
For example:
if (isArray(condition)) {
var dup_condition = condition.slice();
// ...
}
In javascript the objects are passed by reference. In other words, in handleCondition dup_condition still points to the same array. So, if you change it there you are actually changing the passed object. Here is a short example which illustrates the same thing:
var globalData = {
arr: [10, 20]
};
var handleData = function(data) {
var privateData = data;
privateData.arr.shift();
privateData.arr.push(30);
}
console.log(globalData.arr);
handleData(globalData);
console.log(globalData.arr);
The result of the script is:
[10, 20]
[20, 30]
http://jsfiddle.net/3BK4b/

Phased execution recursive cycle in Node.js

I'm new to Node.js and asynchronous programming in general, and I have a problem. I need for continuous work server in a recursive loop, which steps would be implemented consistently, and each stage should consist of an asynchronous receive data over websotskets (this is already has understood), or go to next stage on the expiration of timer.
I am aware that is it not a trivial task, but how to implement it, what libraries can help?
I tried to understand the Step.js and Node.js events, this is what i need?
If I wrote all this in sync:
//CODE FOR STACKOVERFLOW
var previous_round = 'qwerty';// this is string, selected in previous cycle
var round_users = users;// 'users' is array of soceket.id all connected users and here a put them into new array 'round_users' with sockets for current cycle
io.sockets.emit('users_round', { number: round_users.length }); // send to users information about number of users in new round
for (var i = 0; i < round_users.length; i++) {
io.sockets.socket(round_users[i]).emit('round', { text: previous_round });//for each socket in 'round_users' emit event to enter chat message
} // and send selected in previous round message
var messages = []; // array of users messages
//now we must listen users events and start timer for next stage
//listen users events
for (var i = 0; i < round_users.length; i++) {
io.sockets.socket(round_users[i]).on('message', function (data) {
messages[messages.length] = data.text;//write text from users to array
if (messages.length == round_users.length) { /* GO TO NEXT STAGE */ } // it's after previous operation in this function
});
}
//or set timeout
setTimeout(/* GO TO NEXT STAGE */,15000);
for (var i = 0; i < round_users.length; i++) {
io.sockets.socket(round_users[i]).emit('voting', { messages_array: messages });//for each socket in 'round_users' emit event to vote perfect chat message
} // and send messages, which they send to server
//i'm not quite sure about this decision of vote counting :-)
var votes = []; //array with users votes
for (var i = 0; i < messages.length; i++) {
votes[i] = 0;
}
//now we must listen users events and start timer
//listen users events
for (var i = 0; i < round_users.length; i++) {
io.sockets.socket(round_users[i]).on('vote', function (data) {
votes[data.number]++;//increment selected message
if (votes.length == messages.length) { /* GO TO NEXT STAGE */ } // it's after previous operation in this function
});
}
//or set timeout
setTimeout(/* GO TO NEXT STAGE */,10000);
var max_id = 0; //now select max number from 'votes' array
for (var i = 0; i < votes.length; i++) {
if (votes[i]>votes[max_id]) {max_id = i;} //yet without the same values
}
var previous_round = messages[max_id]; //set up string value of selected message
//restart cycle
this code on pastebin with syntax highlighting
Recursion in the most basic sense is just a function that calls itself over and again, but in this instance with node you may want to utilize process.nextTick() to allow other events to occur during this recursion.
Here is a simple example of one way you might be able to go about it:
function someFunction(args, callback) {
// Check requirements
if (typeof callback !== 'function') return console.log('Callback required');
if (typeof args === 'undefined') return callback('Arguments required');
// Set defaults
var err = false;
var result = false;
// Do other stuff
anotherFunctionWithAcallback(args, function(err, result) {
if (err) return callback(err);
// This way, the nextTick only occurs after processing completes
return callback(err, result);
}
}
(function loop(stage) {
// When the stage argument is not provided, default to stage 1
stage = (typeof stage === 'undefined') ? 1 : stage;
switch(stage) {
case 1:
/* stage 1 */
someFunction(args, function(err, result){
process.nextTick(loop(2));
});
break;
case 2:
/* stage 2 */
someFunction(args, function(err, result){
process.nextTick(loop(3));
});
break;
case 3:
/* stage 3 */
someFunction(args, function(err, result){
// No stage argument, restart at stage 1 by default
process.nextTick(loop());
});
break;
}
})(); // Execute this function immediately

Resources