Undefined 'callback' with Async Waterfall / Node.js - javascript

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.

Related

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

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

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

How to pass scope to callback functions in nodejs?

Im using nodejs and the xml2js module. I'm reading an XML file and try to emit an event after the xml is converted to an json object. My code looks like this :
var fs = require('fs'),
util = require('util'),
events = require('events'),
xml2js = require('xml2js');
var CIRCUITMODELSFILENAME = "ControlCircuitModels.xml";
var CIRCUITPARTMODELSFILENAME = "ControlCircuitParts.xml";
var circuitModels, circuitPartModels;
function ModelController() {
if (false === (this instanceof ModelController)) {
return new ModelController();
}
events.EventEmitter.call(this);
};
util.inherits(ModelController, events.EventEmitter);
ModelController.prototype.load = function (baseDir) {
var parser = new xml2js.Parser({
normalize: true,
trim: true,
explicitArray: false
});
fs.readFile(baseDir + "/" + CIRCUITMODELSFILENAME, function (err, data) {
parser.parseString(data, function (err, result) {
circuitModels = result;
console.log('circuit models loaded');
parser.reset();
fs.readFile(baseDir + "/" + CIRCUITPARTMODELSFILENAME, function (err, data) {
parser.parseString(data, function (err, result) {
circuitPartModels = result;
console.log('circuit part models loaded');
moduleReady = true;
this.emit("modelsloaded", null);
});
});
});
});
};
// public interface
exports.ModelController = ModelController;
Problem is that the scope when emitting the event is lost.
this.emit("modelsloaded", null);
this hasn't inherited the emit from EventEmitter.
How can I pass the scope to the parser.parseString function?
Thanks
Chris
Not sure if this is the best solution, bis this works (doesn't look straight foreward):
fs.readFile(baseDir + "/" + CIRCUITMODELSFILENAME, function (err, data) {
parser.parseString(data, function (err,result) {
circuitModels = result;
parser.reset();
fs.readFile(baseDir + "/" + CIRCUITPARTMODELSFILENAME, function (err, data) {
circuitPartModels = result;
console.log('circuit models loaded');
parser.parseString(data, function (err, result) {
console.log('circuit part models loaded');
this.emit("modelsloaded", null);
moduleReady = true;
circuitPartModels = result;
}.bind(this));
}.bind(this));
}.bind(this));
}.bind(this));

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