Nodejs how to access callback variable outside the function? - node.js

This is my code want to access callback variable newID outside calling-function. I want to insert bulk data into mongodb using batch with auto incremented id instead of default object
for (var i = 0; i < sizeOfResult; ++i) {
var newKey = {}; //Main json array
newKey = {
date: result[i]['date'],
issue: result[i]['issue'],
status: result[i]['status']
};
getNextSequenceValue("inventoryid",db, function(err, newID) {
newKey["_id"] = newID; <!-- try to add/assign callback variable(newID) into newKey -->
});
console.log("newKey: %j", newKey); <!-- but unable to get access callback variable(newID) here below-->
batch.insert(newKey);
}
// This is my called function
function getNextSequenceValue(name,db,callback) {
var ret = db.collection('counters_inv').findAndModify({ _id: name },null,{ $inc: { sequence_value: 1 } }, {new: true},
function(err,doc ) {
if(err){
return callback(err) // callback on error
}
callback(null, doc.value.sequence_value); // callback on success
});
}

Look at this code, you just need to put the variable outside and it works:
let i = 1
function functionwithcallback(callback) {
console.log(i)
i++
callback(i)
}
for (let j = 1; j <= 10; j++) {
functionwithcallback(() => {
if (j == 10)
console.log('done')
})
}

I'm not sure what the overall objective is, but the reason your newKey variable is not set correctly is because where it is being used has executed before the variable is set. In your example, your for loop is going to completely finish running kicking off a bunch of getNextSequenceValue() method calls that will eventually come back and run the callback code. It does not wait on the getNextSequenceValue function to finish before continuing the loop.
Solution: moving the console.log() and batch.insert() into the callback.
Here's an example that would execute in the correct order.
var keys = [];
for (var i = 0; i < sizeOfResult; ++i) {
var newKey = {
date: result[i]['date'],
issue: result[i]['issue'],
status: result[i]['status']
};
getNextSequenceValue("inventoryid", db, function(err, newID) {
newKey["_id"] = newID;
keys.push(newKey);
if (keys.length === sizeOfResult) {
console.log("keys: %j", keys);
batch.insertAll(keys);
}
});
}
function getNextSequenceValue(name, db, callback) {
db.collection('counters_inv').findAndModify({ _id: name }, null, { $inc: { sequence_value: 1 } }, {new: true},
function(err,doc) {
if(err){
return callback(err);
}
callback(null, doc.value.sequence_value);
});
}

Related

Array is empty, no data after for loop added

I'm using redis and nodejs for the first time, without success. Trying to loop insert data but got an empty array.
const redis = require("redis");
const client = redis.createClient({
retry_strategy: function(options) {
if (options.error && options.error.code === "ECONNREFUSED") {
// End reconnecting on a specific error and flush all commands with
// a individual error
return new Error("The server refused the connection");
}
if (options.total_retry_time > 1000 * 60 * 60) {
// End reconnecting after a specific timeout and flush all commands
// with a individual error
return new Error("Retry time exhausted");
}
if (options.attempt > 10) {
// End reconnecting with built in error
return undefined;
}
// reconnect after
return Math.min(options.attempt * 100, 3000);
},
});
var data_value = {
id: '235235',
totalrv: 'WAIT',
product: 'productName2',
url: 'url2',
process: 'proc',
task: 'msg'
};
client.set("key0", JSON.stringify(data_value));
client.set("key1", JSON.stringify(data_value));
client.set("key2", JSON.stringify(data_value));
client.set("key3", JSON.stringify(data_value));
client.set("key4", JSON.stringify(data_value));
//client.get("key2", redis.print);
var logger_data = {
logger: []
};
client.keys('*', function (err, keys) {
if (err) return console.log(err);
for(var i = 0, len = keys.length; i < len; i++) {
var values_v = client.get(keys[i].toString(), function(err, val) {
// console.log(logger_data);
// data is exist ...
logger_data.logger.push(JSON.parse(val));
});
}
});
// empty data
console.log(logger_data);
I wrote 2 print data result, in the loop it's working ok, but end of function, there are no data in array.
you can call a function inside the callback if you want to print the logger_data with values outside the asynchronous callback, like this
function printLoggerData(){
console.log(logger_data);
}
client.keys('*', function (err, keys) {
if (err) return console.log(err);
for(var i = 0, len = keys.length; i < len; i++) {
var values_v = client.get(keys[i].toString(), function(err, val) {
// console.log(logger_data);
// data is exist ...
logger_data.logger.push(JSON.parse(val));
// calling the function here so that it contains the latest values
printLoggerData();
});
}
});
Ok, with help of CertainPerformance, it's now working in asynchronous.
Thank you very much ...
async function getMessage () {
var logger_data = {
logger: []
};
return new Promise(function(resolve, reject) {
client.keys('*', function (err, keys) {
if (err) return console.log(err);
for(var i = 0, len = keys.length; i < len; i++) {
var values_v = client.get(keys[i].toString(), function(err, val) {
logger_data.logger.push(JSON.parse(val));
resolve(logger_data);
});
}
});
});
}
async function main() {
let message = await getMessage();
console.log(message);
}
main();

returning Mongoose query result from Async call

I'm working on a problem where I need to query the db for an instance of a Voter, and use that instance to update an Election, returning to the original function whether that update was successful or not. My code currently looks like this:
function addCandidatesToElection(req, res) {
let electionName = req.body.electionName;
let candidates = req.body.candidates;
let addedCandidatesSucessfully = true;
for(let i=0; i<candidates.length; i++) {
addedCandidatesSucessfully = _addCandidateToElection(electionName, candidates[i]);
console.log("added candidates sucessfully:" + addedCandidatesSucessfully);
}
if(addedCandidatesSucessfully) {
res.send("createElection success");
} else {
res.send("createElection fail");
}
}
which calls this function:
function _addCandidateToElection(electionName, candidateName) {
async.parallel(
{
voter: function(callback) {
Voter.findOne({ 'name' : candidateName }, function(err,voter) {
callback(err, voter);
});
}
},
function(e, r) {
if(r.voter === null){
return 'Voter not found';
} else {
Election.findOneAndUpdate(
{'name': electionName },
{$push: { candidates: r.voter }},
{new: true},
function(err, election) {
if(err){ return err; }
return (election) ? true : false;
});
}
}
);
}
I've already tried printing out the Voter instance(r.voter) to check if it exists (it does), and also printing out the election object returned by the mongoose call, which also works. However, I'm getting a null value in the
addedCandidatesSucessfully = _addCandidateToElection(electionName, candidates[i]);
line, regardless of the result of the call. I think it has to do with the mongoose call returning a local value which is never returned to the function that called _addCandidateToElection, but I don't know how I should return that. I've tried putting control flags such as
let foundAndUpdatedElection = false;
on the first line of _addCandidateToElection and updating it inside the Mongoose query's callback, but apparently it doesn't change.
How should I return the result of the query to the addCandidatesToElection function?
You should probably 'promisify' your code to help you better deal with the asynchronous nature of js. Try the following instead of your example:
function findVoter(candidateName) {
return new Promise(function(resolve, reject) {
Voter.findOne({ 'name' : candidateName }, function(err,voter) {
if(error) {
reject(error);
} else {
resolve(voter);
}
});
});
}
function addCandidateToElection(electionName, candidateName) {
return findVoter(candidateName).then(function(voter) {
return new Promise(function(resolve, reject) {
Election.findOneAndUpdate(
{'name': electionName },
{$push: { candidates: voter }},
{new: true},
function(err, election) {
if (err) {
reject(err);
} else {
resolve(!!election);
}
});
});
}
function addCandidatesToElection(req, res) {
let electionName = req.body.electionName;
let candidates = req.body.candidates;
let addedCandidatesSucessfully = true;
let candidatePromiseArray = [];
for(let i=0; i<candidates.length; i++) {
candidatePromiseArray.push(addCandidateToElection(electionName, candidates[i]));
}
Promise.all(candidatePromiseArray)
.then(function(results) {
console.log(results);
res.send('create election success');
})
.catch(function(error) {
console.error(error);
res.send('failed');
});
}
You will also no longer need to use the async library because promises are now native in ES6

For loop not working properly in node js

I want to insert multiple data by for loop , but this code add 2 or 3 data only , then its continue loading loading but nothing happen ...
router.post('/addclient' , function (req , res){
var finished = false;
var user = req.body.username;
for(var i = 0 ; i <30 ; i++){
req.body.username = user + i ;
objDB.insert("clientList" ,req.body, function(err, data){
//if(err) console.log(err);
if(i>20){
finished = true;
}
});
}
if(finished){
res.redirect('/client/client-list');
}
});
you are doing it wrong. insert is asynchronous, so when insert for i=1 completes and callback gets called, maybe i is equal to 20 and more and you can't guess it. you are missing synchronous and asynchronous concepts and differences.
here is two solutions for your problem :
first, adding 30 items at once :
var clientsList = [];
for (var i = 0 ; i < 30 ; i++) {
clientsList[i] = Ith client object; // create object you want to insert for client i
}
// for example insertAtOnce is a function to insert your list
objDB.insertAtOnce(clientsList,function(err,callback) {
if (err) console.log("error : ", err);
res.redirect('/client/client-list');
});
second, using bind
function insertCallback(itr, res, err,callback) {
if (itr == 29) {
res.redirect('/client/client-list');
}
}
----
for(var i = 0 ; i <30 ; i++){
req.body.username = user + i ;
objDB.insert("clientList" ,req.body,insertCallback.bind(null, i, res);
}
Would be best if you use the async node.js library which provides control flows like running each function in series. You could use the async.whilst() method and restructure your code as:
router.post('/addclient', function(req, res) {
var user = req.body.username,
count = 0,
result = { finished = false };
async.whilst(
function () { return count < 30; },
function (callback) {
count++;
req.body.username = user + count.toString();
objDB.insert("clientList", req.body, function(err, data){
if (err) { return callback(err); }
result["data"] = data;
if( count > 20 ) result.finished = true;
callback(null, result);
});
},
function (err, result) {
if (err) { /* handle err */};
if (result.finished) res.redirect('/client/client-list');
}
);
});
You have a callback called after running insert function. It runs not exactly after running insert, and not in loop(it asynchronous). So in your code res.redirrect will never be called, because finished will be true after all loop will run. If you don't need to have all this inserts finished before answer, you can go this way:
if(i == 29){
res.redirect('/client/client-list');
}
Otherwise you need to create some queue, for example, i'm using async library https://github.com/caolan/async to create queues and running callbacks after queue ends.
router.post('/addclient', function(req, res) {
var finished = false;
var user = req.body.username;
var i = 0;
while( true ) {
if ( i > 20 ) {
res.redirect('/client/client-list');
}
req.body.username = user + i;
var promise = new Promise();
objDB.insert("clientList", req.body, function(err, data) {
promise.resolve();
});
promise.then(function() {
i++;
continue;
});
}
});
I have written it from the top of my head. But the basic idea here is that you need a loop to run infinitely, then resolve a promise; the resolution triggers continuing the loop, and increment the counter.
Use Promise. I would have done something like below
router.post('/addclient' , function (req , res){
var finished = false;
var promise = [];
var user = req.body.username;
for(var i = 0 ; i <30 ; i++) {
req.body.username = user + i;
promise.push(new Promise(resolve, reject) {
objDB.insert("clientList" ,req.body, function(err, data) {
if(err) {
reject(err)
return
}
resolve(data)
})
})
}
Promise.all(promise).then(function() {
finished = true
})
if(finished){
res.redirect('/client/client-list');
}
});

Q Promise Nodejs how to resolve in loop

i have code written in nodejs make me confusying using Q Promises
theFunction()
.then(function(data) {
var deferred = Q.defer()
var result = [];
for(i=0; i < data.length; i++) {
secondFunc(data.item)
.then(function(data2) {
data.more = data2.item
});
result.push(data);
}
deferred.resolve(result);
deferred.promise();
});
i want data in second function inside loop can push into result
so my previous data is like this
[
{
id: 1,
item: 1,
hero: 2
},
{
id: 1,
item: 1,
hero: 2
}
]
and so like this
[
{
id: 1,
item: 1,
hero: 2,
more: {
list: 1
}
},
{
id: 1,
item: 1,
hero: 2,
more: {
list: 4
}
}
]
I've tried several ways start by entering the command
deferred.resolve (); statement in the loop and only showing 1 data
have any solution ?
Instead of a deferred.resolve() on an array which will resolve immediately, use Q.all which waits for an array of promises:
theFunction()
.then(function(data) {
var result = [];
for(var i=0; i < data.length; i++) (function(i){
result.push(secondFunc(data[i].item)
.then(function(data2) {
data[i].more = data2.item;
return data[i];
}));
})(i); // avoid the closure loop problem
return Q.all(result)
});
Or even better:
theFunction()
.then(function(data) {
return Q.all(data.map(function(item)
return secondFunc(item)
.then(function(data2) {
item.more = data2.item;
return item;
});
});
});
I know this is a older post but I've the same problem and did not found any solution. Maybe someone here find a good solution very fast.
function CompareTeamspeakClients(forumUsers) {
var promises = [];
var tsClient = new TeamSpeakClient("127.0.0.1", 10011);
tsClient.send("login", {
client_login_name: "serveradmin",
client_login_password: "M+h8YzUA"
}, function(err, response){
if (err) deferred.reject(err);
});
tsClient.send("use", {
port: 9987
}, function(err, response){
if (err) deferred.reject(err);
});
forumUsers.forEach(function(user, index){
var deferred = Q.defer();
tsClient.send("clientdbfind", ["uid"], {
pattern: user.tsid
}, function(err, response){
if (err) deferred.reject(err);
if (response) {
tsClient.send("clientdbinfo", {
cldbid: response.cldbid
}, function(err, response){
if (err) deferred.reject(err);
forumUsers[index]['tsdbid'] = response.client_database_id;
forumUsers[index]['tsnickname'] = response.client_nickname;
forumUsers[index]['tslastconnected'] = response.client_lastconnected;
deferred.resolve(forumUsers);
});
}
});
promises.push(deferred.promise);
});
console.log(promises);
return Q.all(promises);
}

I am not able to get value after for loop scope in node.js

I am not able to get value after for loop scope in node.js
for (var i = 0; i < project_list[0].KeywordsId.length; i++) {
keywordModel.find({
_id: project_list[0].KeywordsId[i]
}, function(err, keywords) {
keywordsArray.push(keywords[0].text);
});
}
console.log(">>>>>>>>>>>>>>>>>>>>>>", keywordsArray);
you have to write the asyncronous code to perform this action. in your case it will be like this
https://github.com/caolan/async#map
var async = require('async'),
keywords;
async.map(project_list[0].KeywordsId,
function(id, cb){
keywordModel.find({ _id: id }, cb);
}, function(error, keywordsFound){
if(error){
throw error;
} else {
keywords = keywordsFound;
}
});
setTimeout(function(){
//we have to wait for keywords to be found
console.log(keywords);
}, 1000);

Resources