what is wrong with my callback? - javascript

This is the file "path-info.js" that has 2 functions: pathInfo & callback. Pathinfo collects all info about file from path in the object "info", callback gets that object and returns it. Code:
"use strict";
const fs = require("fs");
let getInfo = function (err, someObject) {
if (err) throw err;
return someObject;
};
function pathInfo(path, callback) {
let info = {};
info.path = path; // write path info
fs.stat(path, (err, type) => { // write type info
if (type.isFile()) {
info.type = "file";
}
if (type.isDirectory()) {
info.type = "directory";
} else {
info.type = "undefined";
}
});
fs.stat(path, (err, type) => { // write content info if it is file
if (type.isFile()) {
fs.readFile(path, "utf8", (err, content) => {
info.content = content;
});
} else {
info.content = "undefined";
}
});
fs.stat(path, (err, type) => { // write childs if it is directory
if (type.isDirectory()) {
fs.readdir(path, (err, childs) => {
info.childs = childs
});
} else {
info.childs = "undefined";
}
});
getInfo(null, info); // callback returns object "info"
}
module.exports = pathInfo;
I use my callback function as it was shown, for instance, here: nodeJs callbacks simple example. Still, this code does not work and I do not why.
I call this code using file "test.js", here is the code:
const pathInfo = require('./path-info');
function showInfo(err, info) {
if (err) {
console.log('Error occurred');
return;
}
switch (info.type) {
case 'file':
console.log(`${info.path} — is File, contents:`);
console.log(info.content);
console.log('-'.repeat(10));
break;
case 'directory':
console.log(`${info.path} — is Directory, child files:`);
info.childs.forEach(name => console.log(` ${name}`));
console.log('-'.repeat(10));
break;
default:
console.log('Is not supported');
break;
}
}
pathInfo(__dirname, showInfo);
pathInfo(__filename, showInfo);
So the logic is that I need to give my callback the object that contains some info about directory or file. Depending on that, some console.logs will be displayed.
Any help will be appreciated!
UPD: updated the code, just renamed my "callback" function to "getInfo".

A callback is a function you pass as parameter to another function.
In your case, your second parameter is function showInfo which is your callback. Your function pathInfo accepts two parameters, second is showInfo.
So when you call it, you execute code within showInfo with some parameters, usually err, then something else.
In your case, you name second parameter "callback" in showInfo, so you have to execute it with asked parameters (err and info).
Example:
function myfunc (parameter,cb) {
cb(null,{});
}
myfunc("one", function (err,res) {
console.log(err);
});
where "cb" in "myfunc" is function sent as second parameter.
It can be write like this as you did it:
var cb = function (err,res) {
console.log(err);
}
myfunc("one",cb);

In case anyone is interested.. I found a solution and it works! As #ADreNaLiNe-DJ correctly stated, my callbacks are not finished when i call getInfo callback to return info object. So the way out is to change my level of abstraction: all i´ve done is pasted my callbacks inside functions. See that code:
"use strict";
const fs = require("fs");
let pathInfo = (path, callback) => {
let info = {};
info.path = path;
fs.stat(path, (err, type) => {
if (err) throw err;
if (type.isFile()) {
info.type = "file";
fs.readFile(path, "utf8", (err, content) => {
info.content = content;
info.childs = undefined;
callback(err, info);
});
}
if (type.isDirectory()) {
info.type = "directory";
fs.readdir(path, (err, childs) => {
info.childs = childs;
info.content = undefined;
callback(err, info);
});
}
});
};
let showInfo = (err, info) => { // Отсюда и ниже вставлен код из текста
if (err) { // из домашнего задания
console.log('Возникла ошибка при получении информации');
return;
}
switch (info.type) {
case 'file':
console.log(`${info.path} — является файлом, содержимое:`);
console.log(info.content);
console.log('-'.repeat(10));
break;
case 'directory':
console.log(`${info.path} — является папкой, список файлов и папок в ней:`);
info.childs.forEach(name => console.log(` ${name}`));
console.log('-'.repeat(10));
break;
default:
console.log('Данный тип узла не поддерживается');
break;
}
};
pathInfo(__dirname, showInfo);
pathInfo(__filename, showInfo);
PS: sorry for Russian console.logs, hope it won't disturb you (they don't bring any value anyway into understanding how it works)

Related

value not setting in the variable which is defined outside in NodeJS

I'm new to NodeJS so please apologize if below code is not up-to the standard. I would like to access isSuccess value outside of this function stepfunctions.listExecutions
I tried below code but I'm getting the value is undefined not getting the expected output. I did some internet search and came to know in NodeJS we can't set the value which is defined in globally but I've use case and I'm pretty sure this is a common case for others too - where I would like to access this isSuccess value after my execution.
const AWS = require('aws-sdk');
const stepfunctions = new AWS.StepFunctions({
region: process.env.AWS_REGION
});
var params = {
stateMachineArn: 'arn:aws:states:us-west-1:121:stateMachine:test',
maxResults: '2',
nextToken: null,
statusFilter: 'SUCCEEDED'
};
var isSuccess
stepfunctions.listExecutions(params, function (err, data) {
if (err) console.log(err, err.stack);
else
data.executions.forEach(function (result) {
let params = {
executionArn: result.executionArn
};
stepfunctions.describeExecution(params, function (err, data) {
if (err) console.log(err, err.stack);
else {
isSuccess = 'true'
}
});
});
console.log('isSuccess: ' +isSuccess)
});
Expected output:
isSuccess: true
But I'm getting
isSuccess: undefined
Could you please help me to resolve this issue. Appreciated your help and support on this.
This is how you can wrap it on promise
let isSuccess;
const listExecute = function(params) {
return new Promise((resolve, reject) => {
stepfunctions.listExecutions(params, function (err, data) {
if (err) reject(err);
else
data.executions.forEach(function (result) {
let params = {
executionArn: result.executionArn
};
stepfunctions.describeExecution(params, function (err, data) {
if (err) reject(err);
else {
resolve(true)
}
});
});
});
})
}
async function getOutout(params) {
try {
isSuccess = await listExecute(params);
console.log(isSuccess, 'Output')
} catch(e) {
console.log(e)
}
}
getOutout(params)
Also you can export the listExecute so that you can use this function outside of this file.
module.exports = {listExecute}

Async.waterfall function gets called twice

I have an async waterfall Array where the function otherIngrLists() is the 3rd to be executed. Every function before that worked fine.
function otherIngrLists(userslist, callback){
collection = db.get('ingrList');
collection.find({"userid":{$ne:userid}},{},function(err,docs){
if(!err){
var otherLists = docs;
var otherListsCount = docs.count();
console.log(otherListsCount);
callback(null, otherLists, otherListsCount, userslist);
} else {
callback(err, null);
}
});
},
The Problem is that this function is called twice. I assured this with a simple console.log().
How did I manage to call this function again? Did I get the concept of callbacks wrong as I use them to be passed on to the next function?
Also after this function executing twice an error ist thrown. It has nothing to to with this problem though and I will concern my self with that later.
Thank you for your time!
Waterfall Array in router.get:
router.get('/:userid', function(req, res) {
var db = req.db;
var collection;
var userid = req.params.userid;
async.waterfall(
[
function getIngrList(callback, userid) {
var route = 'http://localhost:3000/users/zutatenliste/'+userid;
request(route, function(err, response, body){
if (!err && response.statusCode === 200) {
var userlist = body;
callback(null, userlist);
} else {
callback(err, null);
return;
}
});
},
function otherIngrLists(userlist, callback){
collection = db.get('zutatenListe');
console.log(userid);
collection.find({"userid":{$ne:userid}},{},function(err,docs){
if(!err){
var otherLists = docs;
var otherListsCount = docs.count();
callback(null, otherLists, otherListsCount, userlist);
} else {
callback(err, null);
}
});
},
function pushInArray(otherLists, otherListsCount, userlist, callback){
console.log("test");
...
...}
}
}
Edit 1: --Also either if cases are executed, first the true one then the false--
// Does not happen anymore
Edit 2: Added the whole Thing until the problematic function
Please provide some Additional details as this function seems perfect and No, You haven't misunderstood the concept of callback you are using it correctly.
Structure of Async Waterfall
var create = function (req, res) {
async.waterfall([
_function1(req),
_function2,
_function3
], function (error, success) {
if (error) { alert('Something is wrong!'); }
return alert('Done!');
});
};
function _function1 (req) {
return function (callback) {
var something = req.body;
callback (null, something);
}
}
function _function2 (something, callback) {
return function (callback) {
var somethingelse = function () { // do something here };
callback (err, somethingelse);
}
}
function _function3 (something, callback) {
return function (callback) {
var somethingmore = function () { // do something here };
callback (err, somethingmore);
}
}
so, in waterfall you can pass the values to the next function and your 3rd function is correct.
Edited
async.waterfall(
[
//can not give userId as second parameter
function getIngrList(callback) {
//if you want userId you can pass as I shown above or directly use here if it's accessible
var route = 'http://localhost:3000/users/zutatenliste/'+userid;
request(route, function(err, response, body){
if (!err && response.statusCode === 200) {
var userlist = body;
callback(null, userlist);
} else {
callback(err, null);
// return; no need
}
});
},
function otherIngrLists(userlist, callback){
collection = db.get('zutatenListe');
console.log(userid);
collection.find({"userid":{$ne:userid}},{},function(err,docs){
if(!err){
var otherLists = docs;
var otherListsCount = docs.count();
callback(null, otherLists, otherListsCount, userlist);
} else {
callback(err, null);
}
});
},
function pushInArray(otherLists, otherListsCount, userlist, callback){
console.log("test");
...
...}
As said you can not pass userId as last parameter over there. Let me know if you still get the same error.
First you need to declare you function:
function myFuntion(userId, callback) {
async.waterfall([
function(callback) {
//do some thing here
callback(null, userlist);
}, function(userId, callback) {
//do something here
callback(null, orderList, orderListCount, userlist);
}
], function(err, orderList, orderListCount, userlist) {
if(err)
console.log(err);
else
callback(orderList, orderList, userlist);
})
}
After that you can use function:
myFuntion(userId, function(orderList, orderListCount, userlist) {
console.log(orderList);
console.log(orderListCount);
console.log(userlist);
})

Getting error can not get header after they send when read files from directory?

I am trying to get the name and created date of the files. In the code below it throws error when I call the api. It is reading the directory and printing all the file names but it's not sending back to callback. Any idea what is implemented wrong?
service.js
var fs = require('fs');
var path = require('path');
var async = require('async');
var currentDate = new Date();
var objToReturn = [];
var logsDirectory = './logs'
function readDirectory(env, callback) {
fs.readdir(logsDirectory + '/' + env, function(err, files) {
// loop through each file
async.eachSeries(files, function(file, done) {
var dirPath = logsDirectory + '/' + env;
var filePath = path.join(dirPath, file);
var fileInfo = {};
fs.stat(filePath, function(err, stats) {
if (err) {
console.info("File doesn't exist");
} else {
fileInfo.fileDate = stats.birthtime;
fileInfo.filename = file;
objToReturn.push(fileInfo);
done();
}
});
});
},
function(err) {
if (err) {
console.info('error', err);
return;
}
// when you're done reading all the files, do something...
console.log('before Callback', objToReturn);
callback(objToReturn);
});
}
exports.readDirectory = readDirectory;
app.js
var stDirectory = require('./app/serverfiles/stDir');
app.get('/getAllFiles',function(req,res){
var env = req.query.env
console.log('printing',env);
stDirectory.readDirectory(env,function(files){
res.json(files);
console.log('Api files',files);
});
});
There are a few issues:
instead of passing the "final" handler to async.eachSeries(), you're passing it to fs.readdir(), so callback will never get called;
you're declaring objToReturn outside of the function, which isn't a good idea because multiple requests could be handled in parallel;
you're not handling any errors properly;
you should really use the Node.js callback idiom of calling callbacks with two arguments, the first being errors (if there are any) and the second being the result of the asynchronous operation.
The code below should fix these issues:
function readDirectory(env, callback) {
let objToReturn = [];
fs.readdir(
logsDirectory + "/" + env,
function(err, files) {
if (err) return callback(err);
// loop through each file
async.eachSeries(files, function(file, done) {
var dirPath = logsDirectory + "/" + env;
var filePath = path.join(dirPath, file);
var fileInfo = {};
fs.stat(filePath, function(err, stats) {
if (err) {
console.info("File doesn't exist");
return done(err);
} else {
fileInfo.fileDate = stats.birthtime;
fileInfo.filename = file;
objToReturn.push(fileInfo);
done();
}
});
}, function(err) {
if (err) {
console.info("error", err);
return callback(err);
}
// when you're done reading all the files, do something...
console.log("before Callback", objToReturn);
callback(null, objToReturn);
}
);
}
// To call it:
stDirectory.readDirectory(env, function(err, files) {
if (err) {
res.sendStatus(500);
} else {
res.json(files);
console.log('Api files',files);
}
});
You should also consider using async.mapSeries() instead of async.eachSeries() and using a separate array (objToReturn).

How to use async with other asynchronous modules?

Here is a working test program without async:
var fs = require('fs');
function test() {
var finalResponse = '', response = '';
function showFinalResponse() {
console.log(finalResponse);
}
function processTheFile(err, data) {
if (err) {
finalResponse = 'Could not read the file';
} else {
response += data;
response += '</body></html>';
finalResponse = response;
}
showFinalResponse();
}
function readTheFile(exists) {
if (!exists) {
finalResponse = 'File does not exist.';
showFinalResponse();
} else {
response += '<!DOCTYPE html><html lang="en-US"><head></head><body>';
fs.readFile('file.txt', 'utf8', processTheFile);
}
}
fs.exists('file.txt', readTheFile);
};
test();
Here is my attempt at getting the same program to work with async waterfall. I'm having trouble with how to pass the callbacks around in the async and the fs calls.
var fs = require('fs');
var async = require('async');
function testAsync() {var finalResponse, response = '';
async.waterfall( [
function checkIfTheFileExists(done) {
fs.exists('file.txt', done);
},
function readTheFile(err, exists, done) {
response += '<!DOCTYPE html><html lang="en-US"><head></head><body>';
fs.readFile('file.txt', 'utf8', done);
},
function processTheFile(err, data, done) {
response += data;
response += '</body></html>';
finalResponse = response;
done(null);
} ],
function showFinalResponse(err) {
if (err) {
if (err.code === 'ENOENT') { // intended to test for file is missing.
finalResponse = 'File does not exist.';
} else { // any other errors.
finalResponse = 'Could not read the file';
}
console.log(err);
}
console.log(finalResponse);
}
);
}
testAsync()
I can't get the async version to work. I'm getting confused with where the callbacks go.
fs.exists is an oddball in that it doesn't provide an error parameter to its callback function. Instead it only provides a single exists parameter that indicates whether the file was found or not. Presumably, if there was an error, exists would be false. As such you need to wrap its callback in your own function so that you can provide a separate error parameter to the waterfall callback:
async.waterfall( [
function checkIfFileExists(done) {
fs.exists('file.txt', function(exists) { done(null, exists); });
},
function makeSureFileExists(exists, done) {
...
Note the warning in the docs, however, that fs.exists shouldn't be used, typically.
fs.exists('file.txt', done(null));
This calls done immediately. You need to pass the actual done function to fs.exists:
fs.exists('file.txt', done);
Same for the others.
Here is my final working version (in case it helps anyone else). Thanks again for your help!
var fs = require('fs');
var async = require('async');
var addErrParm = function (err, done) {return function(exists) {
done(err, exists);
}}
function testAsync() {var finalResponse, response = '';
function checkIfTheFileExists(done) {
fs.exists('file.txt', addErrParm(null, done));
}
function readTheFile(exists, done) {
if (!exists) {
done('notFound');
} else {
response += '<!DOCTYPE html><html lang="en-US"><head></head><body>';
fs.readFile('file.txt', 'utf8', done);
}
}
function processTheFile(data, done) {
response += (data || 'The file is empty') + '</body></html>';
finalResponse = response;
done(null);
}
function showFinalResponse(err) {
if (err) {
finalResponse = (err === 'notFound' ? 'File does not exist.' : 'Could not read the file');
}
console.log(finalResponse);
}
async.waterfall([ checkIfTheFileExists,
readTheFile,
processTheFile
], showFinalResponse);
}
testAsync()
So basically, async requires removing the err parameter (first argument) from all functions except the final callback, and it requires adding a callback ('done') as an extra parameter on all functions except the final callback.
Also, if there is no err parameter like with fs.exists, you have to create a function to simulate an err parameter so async can remove it.

node.js and async module error

I am getting an undefined variable in my code and not sure what the error in my code is:
I get client as undefined when I call getClient...
I have a soap client creation singleton and I have:
var mySingleton = (function() {
var soap = require('soap');
var async = require('async');
var instance;
var client;
function init() {
var url = "http://172.31.19.39/MgmtServer.wsdl";
var endPoint = "https://172.31.19.39:9088";
var options = {};
options.endpoint = endPoint;
async.series([
function(callback) {
soap.createClient(url, options, function (err, result){
console.log('Client is ready');
client = result;
client.setSecurity(new soap.BasicAuthSecurity('admin-priv', 'password'));
callback();
});
}
],
function(err) {
if (err)
return next(err);
});
return {
getClient : function() {
console.log("I will give you the client");
**return client;**
},
publicProperty : "I am also public",
};
};
return {
getInstance : function() {
if (!instance) {
instance = init();
}
return instance;
}
};
})();
module.exports = mySingleton;
so my consumer is :
var soapC = mySingleton.getInstance();
var mySoapClient = soapC.getClient();
I get mySingleton.client is undefined.
Why?
Sure there are better solutions than this one, but it shows you that it can be implemented easier (without async, without singleton):
var soap = require('soap');
var client;
var url = "http://172.31.19.39/MgmtServer.wsdl";
var options = {
endpoint: "https://172.31.19.39:9088"
};
module.exports = {
getClient: function (callback) {
if (client) {
callback(null, client);
return;
}
soap.createClient(url, options, function (err, result) {
if (err) {
callback(err);
return;
}
console.log('Client is ready');
client = result;
client.setSecurity(new soap.BasicAuthSecurity('admin-priv', 'password'));
callback(null, client);
});
},
publicProperty: "I am also public"
};
And when using the client:
// using the client
var mySoapServer = require('./path/to/above/code.js');
mySoapServer.getClient(function (err, client) {
if (err) { /* to error handling and return */ }
client.someRequestMethod(myEnvelope, function (err, response) {
// ...
});
});
There might be a problem when your Soap-Clients gets into trouble (there is no logic to reconnect in case of error). For this you could have a look at the source code of Redis-Client, MySQL-Client, MongoDB-Client, ...
Edit
Some comments on the different aproaches:
The Singleton-pattern is not needed here. Node will execute this JS file only once and further requires get only a reference to the exports. There is no need to create an IIFE scope - the variables won't be visible outside, only the exports.
Programming in Node.js is (besides some special cases) an all-async way. If not done consequently, it just doesn't work or fails/succeeds only if you have good/bad luck.
Error handling looks very much like a lot of boilerplate, but it's necessary in most cases.

Categories