Node.js - Callback is not a function - javascript

I am trying to retrieve a var hash to use it in another module. But I have a problem with callback. I have the error "callback is not a function". I use callback because my variable hash is undefined, so I guess it's a problem of asynchronous.
hash.js
var fs = require('fs');
var crypto = require('crypto');
var algorithm = 'sha256';
var hash = function(filename, callback){
var shasum = crypto.createHash(algorithm);
var s = fs.ReadStream(filename);
s.on('data', function(data) {
shasum.update(data)
})
s.on('end', function() {
var hash = shasum.digest('hex')
callback(hash);
})
}
exports.hash = hash;
app.js
app.post('/upload', upload.single('userfile'), function(req, res){
res.cookie('filename', req.file.originalname);
res.cookie('filesize', req.file.size);
var filename = __dirname +'/'+ req.file.path;
console.log(hash.hash(filename))
//res.cookie('hash', hash.hash(filename));
res.redirect('/hash')
})

Your hash() function is defined with this signature:
var hash = function(filename, callback)
That means you need to pass it BOTH a filename and a callback. But, you are passing it only a filename:
console.log(hash.hash(filename))
As you can see in the implementation, the callback is not optional and is the only way to actually get the result like this:
hash.hash(filename, function(result) {
console.log(result);
});

Related

how to pass data from module.export function to an object

I have a simple Node/Express app and am trying to pass data from a javascript function to a template (powered by jade).
The javascript function looks like this:
module.exports = {
getFeatures: function() {
var request = require("request")
// ID of the Google Spreadsheet + Base URL
var spreadsheetID = "abcdefg-123456";
var sheetID = "od6";
var url = "https://spreadsheets.google.com/feeds/list/" + spreadsheetID + "/" + sheetID + "/public/values?alt=json";
//empty array for features
var features = [];
//get the features
request({
url: url,
json: true
}, function (error, response, body) {
if (!error && response.statusCode === 200) {
var data = body.feed.entry;
data.forEach(function(item) {
var obj = {
pub: item.gsx$publication.$t,
date: item.gsx$date.$t,
title: item.gsx$title.$t,
url: item.gsx$url.$t,
}
features.push(obj);
});
console.log("features", features"); //prints array containing all objects to server console
return features;
}
});
}
};
And the main app looks like this:
'use strict';
var express = require('express');
var jade = require('jade');
var gsheets = require("./gsheets.js"); //pulls in module.exports from above
var featuresOld = require('../private/features.json'); //original json pull (a single array of objects)
var port = process.env.PORT || 3000;
var app = express();
// defining middleweare
app.use('/static', express.static(__dirname + '../../public'));
app.set('view engine', 'jade');
app.set('views', __dirname + '/templates');
...
// features route
app.get('/features', function(req, res) {
var path = req.path;
res.locals.path = path;
var features = gsheets.getFeatures(); //attempting to call js function above
res.render('features', {features: features}); //trying to pass data into a template
});
The first function successfully prints an array of objects to the server console, so I think the error lies in how I'm calling it in the main app.js. (Please note, it's only printing when I have it entered as gsheets.getFeatures();, not var features = gsheets.getFeatures();.)
Please also note that the featuresOld variable is an array of objects that has been successfully passed through to a jade tempalte, so the error is not in the res.render('features', {features: features}); line.
I'm sure this is pretty straightforward, but I can't seem to figure it out. Any help is greatly appreciated, thank you.
I'd recommend you to look into Promises (either Native or using a library like Bluebird).
But without using Promises or generators and keeping things simple, you can pass a callback function that will be called only when the values are retrieved. Within this function you can render the template.
(Note that your function currently does not return anything)
module.exports = {
getFeatures: function(callback) {
var request = require("request")
// ID of the Google Spreadsheet + Base URL
var spreadsheetID = "abcdefg-123456";
var sheetID = "od6";
var url = "https://spreadsheets.google.com/feeds/list/" + spreadsheetID + "/" + sheetID + "/public/values?alt=json";
//empty array for features
var features = [];
//get the features
request({
url: url,
json: true
}, function (error, response, body) {
if (!error && response.statusCode === 200) {
var data = body.feed.entry;
data.forEach(function(item) {
var obj = {
pub: item.gsx$publication.$t,
date: item.gsx$date.$t,
title: item.gsx$title.$t,
url: item.gsx$url.$t,
}
features.push(obj);
});
console.log("features", features"); //prints array containing all objects to server console
callback(features); // call the rendering function once the values are available
}
});
}
};
Now in your main app, you just pass a callback to the function
app.get('/features', function(req, res) {
var path = req.path;
res.locals.path = path;
gsheets.getFeatures(function(features) {
res.render('features', {features: features}); //trying to pass data into a template
});
});
Basically, your request function is asynchronous - the request will run in background and the callback function will be called with the value once it's retrieved. In the meantime, the rest of the code will keep running (in your case you'd try to use the value even though it hasn't been retrieved yet).
If you need to do something that depends on that value, then you'd have to put that code in a callback function which would be called when the value is available (as showed above).
Promises provide a nice API for doing that. There are also new features ES6 that helps you better organise asynchronous code.

JS Flow: How to pass a param to a callback without passing it to the caller

I have an object which opens an xml and change some content in it given some parameters.
I divided the object in four functions, open file, parse file, write file and the whole process caller/flow manager: generate xml, which is mainly an async.js waterfall.
The thing is that firs, I have to read the file and pass the contents to the parse function, but to the parse function (the second one) I need to to pass some parameters received at the generate function. How can I do this in a clean way so I don't have to pass the config param to the openXML function but pass it to the parseXML function?
JavaScript code:
var fs = require('fs');
var async = require('async');
var templPathF = __dirname + '/../templates/widget_template/config.xml';
var resulPathF = __dirname + '/../results/widget_template/config.xml';
/**
* #TODO
*
* [X] Find by name and attribute value
* [X] Set content
* [ ] Change attr
* [X] Delete child
* [ ] Add child
*
*/
var pkgmgr = {
// Open XML
openXML: function(pathf, callback){
fs.readFile(pathf, 'utf8', function (err, xmlString) {
callback(null, xmlString);
});
},
// Parse XML
parseXML: function(config, xmlString, callback){
var document = new xmldoc.XmlDocument(xmlString);
document.descendantWithPath("name").val = 'foo';
// Callback
callback(null, xmlString);
},
// Save XML
saveXML: function(xmlString, callback){
console.log(xmlString);
callback(null, xmlString);
},
// Generate XML
// config: name, description
generateXML: function(config, cb){
var self = this;
async.waterfall([
// Init waterfall
function(callback){
callback(null, templPathF);
},
// Open and Read
self.openXML,
// Treat and change
self.parseXML,
// Save new file
self.saveXML
]);
}
};
module.exports = pkgmgr;
use the bind function.
changes in the code
cheers
happy coding :)
var fs = require('fs');
var async = require('async');
var templPathF = __dirname + '/../templates/widget_template/config.xml';
var resulPathF = __dirname + '/../results/widget_template/config.xml';
var pkgmgr = {
openXML: function(pathf, callback) {
fs.readFile(pathf, 'utf8', callback);
},
parseXML: function(config, xmlString, callback){
var document = new xmldoc.XmlDocument(xmlString);
document.descendantWithPath("name").val = 'foo';
callback(null, xmlString);
},
saveXML: function(xmlString, callback){
callback(null, xmlString);
},
generateXML: function(config, cb){
var self = this;
async.waterfall([
async.constant(templPath),
self.openXML,
self.parseXML.bind(self, config), // <------------
self.saveXML
]);
}
};

Node JS Controller - invoking a function that has a callback

My controller is a soap client as shown below
var _ = require('lodash'),
memoize = require('memoize'),
soap = require('soap'),
http = require('http');
var wsdlUrl = 'http://www.proxixnetwork.com/gsert/PxPointGeocode.asmx?WSDL';
var geocode = function(req,res){
var sessionId = null;
soap.createClient(wsdlUrl, function(err,client){
var args = {"username":'user123', "password":'password123'};
client.PxPointGeocode.PxPointGeocodeSoap.Authenticate(args,function(err,result){
res.jsonp(result.AuthenticateResult.SessionID);
})
});
}
exports.authenticate = geocode;
This soap service provides a session id that will be used in subsequent requests. Hence, I wanted to use 'memoize' to cache the method.
I defined a method that wraps around the soap call and 'memoize'ing it but the problem is that the call to soapClient is not happening.
I do not know how to make the call from router wait for the soap call
Note: Also tried async library's waterfall but did not work.
var _ = require('lodash'),
memoize = require('memoize'),
soap = require('soap'),
http = require('http'),
wsdlUrl = 'http://www.proxixnetwork.com/gsert/PxPointGeocode.asmx?WSDL';
var getSession = function () {
var args = {"username": 'user123', "password": 'password123'};
var sessionId = null;
soap.createClient(wsdlUrl, function (err, client) {
console.log('Inside proxix client'); //not printing
client.PxPointGeocode.PxPointGeocodeSoap.Authenticate(args, function (err, result) {
sessionId= result.AuthenticateResult.SessionID;
//if I use res.jsonp() - the call could be made
})
});
return sessionId;
};
var cached = memoize(getSession);
var geocode = function (req, res) {
var sesssionObj = cached();
res.jsonp(sessionObj);
}
exports.authenticate = geocode;
The two issues I'm seeing are :
memoize is a caching mechanism but is in no way going to change the ansynchronous nature of your function. Returning sessionId will not work because it is not set before it gets to that line. You need to use a callback.
You have not specified which arguments you want memoize to use for the caching, nor where you are getting those values. I'm going to assume for this example that it's username, password and wsdlUrl. and that they are set directly in req.
.
var _ = require('lodash'),
memoize = require('memoize'),
soap = require('soap'),
http = require('http');
var getSessionId = function(username,password,wsdlUrl,callback){
soap.createClient(wsdlUrl, function(err,client){
var args = {"username":username, "password":password};
client.PxPointGeocode.PxPointGeocodeSoap.Authenticate(args,function(err,result){
if(err){
return callback(err);
}
callback(null,result.AuthenticateResult.SessionID);
})
});
});
var getSessionIdCached = memoize(getSessionId,{async:true});
var geocode = function(req,res){
getSessionIdCached(req.username,req.password,req.wsdlUrl,function(err,sessionId){
if(err){
//do some error handling, and probably return
}
res.jsonp(sessionId);
});
});
exports.authenticate = geocode;

Node.js Asynchronous read and write

I am having problem with asynchronous file read and write operations. only the last file is written to the server.
js:
function uploadassignment(req, res){
var path;
var multiparty = require("multiparty");
var form = new multiparty.Form();
console.log(req.query);
var filelength = req.query.filecount;
console.log(filelength);
form.parse(req, function(err, fields, files){
console.log(req.body);
for(i=0;i<filelength;i++){
var img = files.file[i];
console.log(img);
console.log('divide');
var fs = require('fs');
fs.readFile(img.path, function(err, data){
var originalfile = img.originalFilename.split('.');
console.log(originalfile);
var file_ext = originalfile[1];
path = "public/assignments/"+img.originalFilename;
console.log(path);
fs.writeFile(path, data, function(error){
if(error)console.log(error);
});
})
}
});
};
This is a common bug caused by using a loop variable without a closure. By the time the callback for the read operation is invoked, the loop has terminated and the index i points to the last element (and hence your img contains the last file). Create a function (a closure) that accepts the index as the parameter and call this function from the loop:
function blah(i) {
var img = files.file[i];
console.log(img);
console.log('divide');
var fs = require('fs');
fs.readFile(img.path, function(err, data){
var originalfile = img.originalFilename.split('.');
console.log(originalfile);
var file_ext = originalfile[1];
path = "public/assignments/"+img.originalFilename;
console.log(path);
fs.writeFile(path, data, function(error){
if(error)console.log(error);
});
})
}
for(i=0;i<filelength;i++) blah(i);
This isn't quite an answer, but it is too long for a comment.
What is not working? The file reading/writing bit of your code works fine:
var fs = require("fs")
img = {
path: "./test.txt",
originalFilename: "test.txt"
}
fs.readFile(img.path, function(err, data){
if(err)console.log(err);
var originalfile = img.originalFilename.split('.');
console.log(originalfile);
var file_ext = originalfile[1];
path = "public/assignments/"+img.originalFilename;
console.log(path);
fs.writeFile(path, data, function(error){
if(error)console.log(error);
});
})
With a directory structure like:
script.js
text.txt
public
assignments
I think your problem might be that you are assigning "fs" locally, then trying to call it from an async function. That might be why only the last one works (maybe.)
Try moving var fs = require('fs'); to the top of your code.

Node.js - Looping through array of URLS one at a time

I am a beginner at node js and I'm trying to write a web scraping script. I got permission from the site admin to scrape their products if I make less then 15 requests a minute. When I started out it used to request all the URLs at once but after some tooling around, I was able to go through each item in the array, but the script doesn't stop when there is no more items in the array? I'm not really happy with my result and feel like there is a better way to do this.
var express = require('express');
var fs = require('fs');
var request = require('request');
var cheerio = require('cheerio');
var app = express();
var async = require('async');
app.get('/scrape', function(req, res){
productListing = ['ohio-precious-metals-1-ounce-silver-bar','morgan-1-ounce-silver-bar']
var i = 0;
async.eachLimit(productListing, 1, function (product, callback) {
var getProducts = function () {
var url = 'http://cbmint.com/' + productListing[i];
request(url, function(error, response, html) {
if(!error){
var $ = cheerio.load(html);
var title;
var json = { title : ""};
$('.product-name').filter(function(){
var data = $(this);
title = data.children().children().first().text();
json.title = title;
})
}
var theTime = new Date().getTime();
console.log(i);
console.log(json.title);
console.log(theTime);
i++;
});
}
setInterval(getProducts,10000);
})
res.send('Check your console!')
})
app.listen('8081')
console.log('Magic happens on port 8081');
exports = module.exports = app;
You aren't calling callback inside the iterator function. Take a look at the docs for eachLimit.

Categories

Resources