How to perserve array value after multiple async callback in node js? - javascript

I am trying to execute multiple callback and at the same time storing value in array , but at the end array return empty.
Here is my code :
var sheetData = [];
async.forEachSeries(req.body.data, function (data, cb) {
sheet.find({accountid: req.body.id}, function (err, doc) {
if (doc == '') {// get the next worksheet and save it
var sheet = new sheet({
accountid: req.body.id,
sheetid: data.sheetid
});
var jsonData = {};
jsonData.sheetid = data.sheetid;
sheet.save(function (err, doc) {
if (!err) {
sheetData.push(jsonData); // trying to push in array , success
console.log("-----sheet data---- : ", sheetData);// data available here
}
});
}
});
cb();
}, function () {
console.log("-----sheet data---- : ", sheetData);// empty array
});
Where I am doing wrong? Can anyone suggest me ?
Or, If any other alternative in nodejs.
Thanks

The callback is being called early. Try following:
var sheetData = [];
async.forEachSeries(req.body.data, function (data, cb) {
sheet.find({accountid: req.body.id}, function (err, doc) {
if (!doc) {
return cb (); //sheet exists, call back early
}
// get the next worksheet and save it
var sheet = new sheet({
accountid: req.body.id,
sheetid: data.sheetid
});
var jsonData = {};
jsonData.sheetid = data.sheetid;
sheet.save(function (err, doc) {
if (!err) {
sheetData.push(jsonData); // trying to push in array , success
console.log("-----sheet data---- : ", sheetData);// data available here
cb (); // all done, now we can call back
}
});
});
}, function () {
console.log("-----sheet data---- : ", sheetData);// lots of sheets
});

Related

Javascript function callback variable scope

I am trying to pass an object variable to a callback
var sql = require('mssql');
var asset_update = function (connection, addr) {
this.connection = connection;
this.addr = addr;
this.addr_long = parseInt(addr, 16);
}
asset_update.prototype.getFromMac = function () {
var ps = new sql.PreparedStatement(this.connection);
ps.input('addr', sql.Binary);
ps.prepare('SELECT asset_id FROM asset_addr WHERE addr = #addr', function (err) {
ps.execute({ addr: this.addr_long }, function (err, recordset) {
ps.unprepare();
console.log(recordset.length);
console.log(this.addr_long);
})
});
}
How can I pass this.addr_long to the ps.execute() callback?
You can use an arrow function to preserve this :
ps.prepare('SELECT asset_id FROM asset_addr WHERE addr = #addr', (err) => {
ps.execute({ mac: this.addr_long }, function (err, recordset) { //this has been preserved
ps.unprepare();
console.log(recordset.length);
console.log(this.addr_long);
})
});
Check out this doc for the ES5 alternatives, which you shouldn't use anymore.
A popular one would be to rename this to that or self. But this is now obsolete. It would look like :
var self = this;
ps.prepare('SELECT asset_id FROM asset_addr WHERE addr = #addr', function (err) {
ps.execute({ mac: self.addr_long }, function (err, recordset) {
ps.unprepare();
console.log(recordset.length);
console.log(that.addr_long);
})
});
As you're new to node/JS, I suggest you read the following resources :
What does "this" mean?
Callbacks overview and tricks
asset_update.prototype.getFromMac = function () {
var that = this;
var ps = new sql.PreparedStatement(this.connection);
ps.input('addr', sql.Binary);
ps.prepare('SELECT asset_id FROM asset_addr WHERE addr = #addr', function (err) {
ps.execute({ mac: that.addr_long }, function (err, recordset) {
ps.unprepare();
console.log(recordset.length);
console.log(that.addr_long);
})
});
}

Fetched data from mongodb viewing using ejs trouble

I m creating a job search portal using nodejs,mongodb,and ejs view engine :
collection "filters" = 'username' 'tags'
collection "alljobs" = 'category' 'jobsdata'
In the following code I'm fetching 'tags' from "filters" collection and comparing with all 'category' in "alljobs" collection. And then all 'tags' array matched with 'category' view their respective 'jobsdata' using ejs view engine.
Problem: Code is working but in browser I can't see all matched category respective jobs only one category jobs data viewed. I am not able to understand where is the problem?
Code:
function processRequest(req,res){
var tags,data,jobsdata = [];
var len;
if(condition)
{....}
else{
var db = new Db('askhere', new Server('localhost', '27017'));
db.open(function (err, db) {
db.authenticate('', '', function (err, result) {
var url = 'mongodb://localhost:27017/askhere';
client.connect(url, function (err, db) {
var col = db.collection('filters');
col.find({username:req.session.userName}).toArray(function (err, items) { // find tags of session user from collection filters
console.log('items: ' + JSON.stringify(items));
items.forEach(function (doc) {
tags = doc.tags; //save tags
});
var col = db.collection('alljobs'); //find all categories jobs matched with tags data in collection alljobs
for(var i=0; i<tags.length;i++){
col.find({category:tags[i]}).toArray(function (err, items1) {
if (items1 == false) {
res.render('mainqa',{uname:req.session.userName,tags:'No Tags Selected',jobsdata:'No Tags Matched !!!',len:0});
}
items1.forEach(function (doc1) {
jobsdata = doc1.jobsdata;
var html = ["url : ", "Posted Date : ", "Job Title : ", "Company : ", "Location : "]
for (var i = 0; i < 25; i++) {
for (var j = 0; j < 5; j++) {
data.push(html[j] + jobsdata[i][j]);
} //Nested for loop
} //for loop covert 2D array in 1D
res.render('mainqa',{uname:req.session.userName,tags:tags,jobsdata:data,len:len});
}); //forEach
}); //col.find collection=alljobs
} //for loop
}); //col.find collection=filters
}); //client connect
}); //db.authenticate
}); //db.open
} //else end
} //function processRequest end
You should stop using callbacks for nesting code of multiple methods unless you want to end up with the Callback Hell. Use the async module to simplify this task, in particular you'd need to use async.waterfall() since you want to run multiple tasks that depend on each other.
The async.waterfall() method allows you to pass values between the functions in a trickle-down manner. It is quite handy when you need the results of a previous function to perform an operation with the next function in the series.
Consider restructuring following this example:
var async = require("async"),
userName = req.session.userName,
locals = {},
url = 'mongodb://localhost:27017/askhere',
db = new Db('askhere', new Server('localhost', '27017'));
async.waterfall(
[
// Open db
function(callback) {
db.open(function (err, db) {
if (err) return callback(err);
callback(null, db);
});
},
// Authenticate and connect
function(db, callback) {
db.authenticate('', '', function (err, result) {
if (err) return callback(err);
client.connect(url, function (err, res) {
if (err) return callback(err);
locals.db = db;
callback(null, locals);
});
});
},
// Query filters collection for tags of session user
function(arg, callback) {
var collection = arg.db.collection("filters");
collection.find({"username": userName}).toArray(function (err, result) {
if (err) return callback(err);
locals.tags = result.map(function(item){ return item.tags; });
callback(null, locals);
});
},
// Query alljobs collection for categories jobs matched with tags data
function(arg, callback) {
var collection = arg.db.collection("alljobs");
collection.find({"category": {"$in": arg.tags}}).toArray(function (err, result) {
if (err) return callback(err);
locals.jobsdata = result.map(function (doc){ return doc.jobsdata; });
callback(null, locals);
});
}
], function(err, result) {
if (err) { /* handle err */ };
res.render("mainqa", {
"uname": userName,
"tags": result.tags,
"jobsdata": result.jobsdata
});
});
);

Undefined 'callback' with Async Waterfall / Node.js

I am trying to get a waterfall working in Node.js using Async but I keep getting the error
callback (null, articleDataSafe, req);
^
TypeError: undefined is not a function
The code is as follows
async.waterfall([
function sanitizeData (articleData, req, callback) {
articleDataSafe = sanitizeArticle(articleData);
console.log('s1');
callback (null, articleDataSafe, req);
},
function _validateData (articleDataSafe, req, callback) {
var errors = validateArticle(articleData);
var err = null;
if(errors.length > 0){
// return error messages back to the browser
err = JSON.stringify({'error': errors, "message": "fail"});
};
console.log('s2');
callback (err, articleDataSafe, req);
},
function _saveArticle (articleDataSafe, req, callback) {
// work out the tags hash
var tags = articleDataSafe['tags'];
var tagsArray = tags.split(",");
tagsArray.sort();
var tagsString = tagsArray.join();
var hashedTags = sha512(tagsString);
articleDataSafe['hashedTags'] = hashedTags;
// then save the articles
var savedArticle = saveArticle(req, articleDataSafe);
console.log('s3');
if(!savedArticle){
var err = JSON.stringify({'error': 'notSaveArticle', 'message': 'fail'});
}
callback (err, articleDataSafe);
},
function _saveTags (articleDataSafe, callback) {
var tagsDone = saveTags(articleDataSafe);
if(tagsDone.length > 0){
// return error messages back to the browser
var err = JSON.stringify({'error': tagsDone, "message": "fail"});
};
console.log('s4');
callback (err, articleDataSafe);
},
function _saveTagSets (articleDataSafe, callback) {
var tagSetsDone = saveTagSets(data);
if(tagSetsDone.length > 0){
// return error messages back to the browser
var err = JSON.stringify({'error': errors, "message": "fail"});
};
console.log('s5');
callback (err, articleDataSafe);
}
], function (error, success) {
var responseMessage = {'error': '', "message": "success"};
if (error) {
responseMessage = error;
}
res.end(JSON.stringify(responseMessage));
});
Can any explain what is going wrong. I have been trying for ages now to get it working with no luck.
Any help will be appreciated.
Thanks,
Cs1h
The first function is passed no other argument than the callback. You'd need to start like this:
async.waterfall([
function sanitizeData (callback) {
// ^^^^^^^^^^ no articleData, no req
articleDataSafe = sanitizeArticle(articleData);
console.log('s1');
callback (null, articleDataSafe, req);
},
…
However I doubt you need async at all.

JavaScript / mongoose - Loop through an array to create array of Objects

At the moment I have a list of Team ID's which I have stored in the array teamIds. I also have a function findByKey in my Team mongoose schema which finds a Team based on its key. As you can see from the code I have to put each new Team.findByKey inside the previous one, if I put it outside of the previous findByKey it doesn't function correctly.
I'm having problems putting the Team.findByKey methods into some sort of for loop which can loop through the array teamIds and for each Team in the array. I am developing the function to create a new tournament, the idea is that you pass an array of teamIds to here, and it loops through each ID, searching for that Team and adding it to a teamsList array in order to create the tournament.
I need to put the last bit of code to create the array of Team objects into the last Team.findByKey method which is my problem. How would I fix this so that I can pass it n amount of teams and find each team and store it as an array of Teams?
This is my code:
app.get('/tournament', function(req, res){
function genMatches(nTeams) {
var matchArray = [];
while (nTeams > 1) {
nTeams = nTeams >> 1;
var matches = [];
for (var i = 0; i < nTeams; ++i) {
matches.push([]);
}
matchArray.push(matches);
}
return matchArray;
}
var teamIds = [
1364472344972,
1363173222886,
1363007586845,
1363007557484
]
var tournamentName = 'My Tournament';
Team.findByKey(teamIds[0], function(err, team1) {
if(err) {
util.log("Error occured");
}
if(!team1) {
util.log("The team does not exist");
}
Team.findByKey(teamIds[1], function(err, team2) {
if(err) {
return util.log("Error occured");
}
if(!team2) {
return util.log("The team does not exist");
}
Team.findByKey(teamIds[2], function(err, team3) {
if(err) {
return util.log("Error occured");
}
if(!team3) {
return util.log("The team does not exist");
}
Team.findByKey(teamIds[3], function(err, team4) {
if(err) {
return util.log("Error occured");
}
if(!team4) {
return util.log("The team does not exist");
}
var teamList = [
team1._id,
team2._id,
team3._id,
team4._id
];
var numRounds = Math.log(teamList.length)/Math.log(2);
var matches = genMatches(teamList.length);
for(var i = 0; i < matches[0].length; i++){
matches[0][i] = [ teamList[i+i], teamList[i+(i+1)] ]
}
var tournament = new Tournament({
name: tournamentName,
teams: teamList,
rounds: numRounds,
matches: matches
});
res.send(tournament);
});
});
});
});
});
Team schema:
'use strict';
var util = require('util');
var mongoose = require('mongoose');
var Schema = mongoose.Schema;
var validatePresenceOf = function(value){
return value && value.length;
};
var toLower = function(string){
return string.toLowerCase();
};
var getId = function(){
return new Date().getTime();
};
/**
* The Team schema. we will use timestamp as the unique key for each team
*/
var Team = new Schema({
'key' : {
unique : true,
type : Number,
default: getId
},
'name' : { type : String,
validate : [validatePresenceOf, 'Team name is required'],
index : { unique : true }
}
});
/**
* Get complete team details for all the teams
*/
Team.statics.getAll = function(cb){
var query = this.find({});
query.sort({key : -1});
return query.exec(cb);
};
Team.statics.findByKey = function(key, cb){
return this.find({'key' : key}, cb);
};
Team.statics.findByName = function(name, cb) {
return this.find({'name' : name}, cb);
};
module.exports = mongoose.model('Team', Team);
You can use the $in operator to look up your four teams in one query:
Team.find({key: {$in: teamIds}}, function (err, teams) {
...
});
If for some reason you need to call findByKey multiple times instead of collapsing it all into a single $in, look at using the async library's each method to do that more cleanly.

Synchronous function calls for nodejs mongodb driver

I have an open source project that deals with mongodb database. I am trying to make a function that queries the database to check if entry exists.
The problem is when if_exists() returning true or false it returns undefined since the mongodb driver function is asynchronous. The file is Query.js and I have tried the solution here to workaround the problem What is the right way to make a synchronous MongoDB query in Node.js? but still I get an undefined result with the get method.
What is the best way to make this work?
The output from the unit tests is as the following:
running unit tests...
add query test
exists tests:
get: undefined
{}
should be true: undefined
get: undefined
{}
should be false:undefined
Captains Logs listening on port 3000
Captains_Logs v0.5.0-21
[ { name: 'rhcp', _id: 50cbdcbe9c3cf97203000002 } ]
[ { name: 'os', _id: 50cbdcbe9c3cf97203000001 } ]
You can browse the whole codes at WeaponXI/cplog
Or for a quick look the query.js code is:
var DB = require('../../lib/db.js').DB;
function methods() {
//query object
var Q = {};
//will act as our private variables to workaround asynchronous functions.
//will delete non-required ones when done -- we don't have to, but just for continuity.
exports.privates = {};
//add tag to collection
Q.add = function(tag) {
if (typeof tag === "string") {
//maybe we are adding a tag by name
var obj = {
name: tag
};
} else if (typeof tag === "object" && tag.name) {
//maybe the tag object was specified, and tag's name was provided
var obj = tag;
}
require('mongodb').connect(DB.mongo_url, function(err, db) {
db.collection('tags', function(err, coll) {
coll.insert(obj, {
safe: true
}, function(err, result) {
console.log(result);
});
});
});
}
var callback = {
_set: function(key, val) {
exports.privates[key] = val;
//console.log(JSON.stringify(privates));
},
_get: function(key) {
console.log("get: "+exports.privates.key);
console.log(JSON.stringify(exports.privates));
return exports.privates[key];
},
_unset: function(key) {
delete privates[key];
}
}
var if_exists = function(query, where, callback) {
require('mongodb').connect(DB.mongo_url, function(err, db) {
db.collection(where, function(err, coll) {
coll.findOne(query, function(e, r) {
//console.log(r);
if (r === null) {
callback._set("does_exist", false);
} else {
callback._set("does_exist", true);
}
});
});
});
var result = callback._get("does_exist");
// delete privates.does_exist;
return result;
}
Q.if_exists = function(query, where) {
if_exists(query, where, callback);
}
return Q;
}
var query = exports.query = methods();
function unit_test_add() {
console.log("add query test");
query.add("os");
query.add({
name: "rhcp"
});
}
function unit_test_if_exists() {
console.log("exists tests:");
console.log("should be true: " + query.if_exists({
name: "os"
}, "tags"));
console.log("should be false:" + query.if_exists({
name: "ossuruk"
}, "tags"));
}
function unit_tests() {
console.log("running unit tests...");
unit_test_add();
unit_test_if_exists();
}
unit_tests();
Solution:
Query.js Query.test.js Gists
Thanks JohnnyHK!
You cannot use an asynchronous result as the return value from a function. It's that simple. You have to deliver the asynchronous result to the caller via a callback that is provided as a parameter to the function (or use futures/promises and effectively defer that step, but that's more involved).
if_exists should look like this instead:
var if_exists = function(query, where, callback) {
require('mongodb').connect(DB.mongo_url, function(err, db) {
db.collection(where, function(err, coll) {
coll.findOne(query, function(e, r) {
//console.log(r);
if (r === null) {
callback(e, false);
} else {
callback(e, true);
}
// You should either close db here or connect during start up
// and leave it open.
db.close();
});
});
});
}

Categories

Resources