Node.js | callback is not defined - node.js

I'm new to Node.js and I'm trying to understand callback. I was wondering what's wrong with my code. I was expecting that it will display all files without their extensions in the directory but all I got is undefined message.
'use strict';
const fs = require('fs');
const postsDirectory = './app/posts';
function listPosts(callback) {
let posts = [];
fs.readdir(postsDirectory, function(err, files) {
if (err) {
callback(err);
} else {
files.forEach(file => {
posts.push(file.split('.').slice(0, -1).join('.'));
});
callback(posts);
};
});
};
console.log(listPosts());
process.exit(0)
Expected Output:
file1
file2
file3

Use callback in following way
'use strict';
const fs = require('fs');
const postsDirectory = './app/posts';
function listPosts(callback) {
let posts = [];
fs.readdir(postsDirectory, function(err, files) {
if (err) {
return callback(err);
} else {
files.forEach(file => {
posts.push(file.split('.').slice(0, -1).join('.'));
});
//check before return here
console.log("postttttttttttttttttt", posts);
return callback(null, posts);
};
});
};
//Use callback function with params
listPosts(function(err,result){
if(err){
console.log(err)
}else{
console.log(result)
}
});
process.exit(0)

Related

In Node js, why is my controller.js not showing the data after invoking a model.js function

I'm new to Nodejs and i've fetched a json file using require and fs, and using MVC , i have a model.js that has the function that reads the json file, but when the controller invokes the model function, the data is not shown (console.log(data) in the controller, but console.logged in the model.js. Here is my code:
controller.js
exports.renderHomePage = (req, res) => {
apiServerModel.loadTeams()
.then(function (data) {
console.log(data);
console.log("This is inside controller")
res.render("index", { // output as string
teamsList: `${JSON.stringify(data, null, 2)}` // A property called teamList to be displayed on the browser
//teamsList: teamView.TeamsView(`${JSON.stringify(data, null, 2)}`)
})
})
.catch(error => console.log(error))
}
model.js
'use strict';
const fs = require('fs');
class TeamsModel {
static async loadTeams() {
try {
await fs.readFile('./json/prov-nodes.json', (err, rawData) => {
if (err) throw err;
let teams = JSON.parse(rawData);
return teams;
//console.log(teams);
});
} catch (error) {
console.log(error)
}
console.log('This is after the read call');
}
}
exports.TeamsModel = TeamsModel;
first of all you should read about Callbacks VS Promises VS Async/Await in node.js.
when you use async/await, don't have a callback so:
use fs/promises instead of fs, because you used await for fs in your loadTeams function.
'use strict';
const fs = require('fs/promises');
class TeamsModel {
static async loadTeams() {
console.log("hi")
try {
let rawData = await fs.readFile('./json/prov-nodes.json')
let teams = JSON.parse(rawData);
return teams;
} catch (error) {
console.log(error)
}
console.log('This is after the read call');
}
}
module.exports = TeamsModel;

Return async function to express router

This may sound dum. But what I am stuck here with is
1: returning the converted JSON file
2: getting the returned object into the route
routes.js
const express = require('express');
const router = express.Router();
const routings = require('./src/services/routings');
router.get('/routings', async(req, res) => {
const routesRes = await routings.getRoutings();
res.end(JSON.stringify(routesRes, null, " ")).catch(function (err) {
console.log(err);
});
});
module.exports = router;
routings.js
const parseXml = require('xml2js')
let data = `<?xml version="1.0" encoding="UTF-8"?>...'
getRoutings = async() => {
await parseXml.parseStringPromise(data).then(function (result) {
console.log('Done');
return result;
})
.catch(function (err) {
console.log(err);
});
}
module.exports = {getRoutings}
Your getRoutings() function does not have a return value. Therefore when you do this:
const routesRes = await routings.getRoutings();
routesRes will always be undefined.
I would suggest this:
getRoutings = () => {
return parseXml.parseStringPromise(data).then(function (result) {
console.log('Done');
return result;
}).catch(function (err) {
console.log(err);
throw err; // make sure error is propagated
});
}

return value not getting logged from module exports

I'm writing a code that uses a library(jsforce) to query on Salesforce and get the records.
Currently, to keep the code clean, I'm separating the index and rest calls file. Here is my code.
var jsforce = require('jsforce');
const uName = 'myId';
const pwd = 'myPwd';
const servKey = 'myKey';
var conn = new jsforce.Connection();
var login = conn.login(uName, pwd, servKey, function (err, res) {
if (err) { return false; }
return true;
});
module.exports = {
myCases: () => {
console.log(`I'm called`);
login.then(() => conn.query(`Select ID, Subject from Case where status='new'`, function (err, openCases) {
if (err) { return console.error(err); }
return openCases;
}));
}
}
and my index file is as below.
const restServices = require('./restServices');
var test = function () {
restServices.myCases((err, data, response) => {
console.log('err')
console.log(err)
console.log('data');
console.log(data);
console.log('response');
console.log(response);
});
}
test();
When I run it, my log prints only I'm called (from restServices.js). but none of the data from my index.js is printed.
also when I add a console.log(openCases), it prints exactly the required data.
please let me know on where am I going wrong in returning the data and how can I fix this.
Thanks
To mycase pass an callback
Example in service.js
Mycase(callback) =>{
// response, err from db then
Callback(err, response)
}
In index.js
Service.mycase((err, response) =>{
Console.log (err, response)
}

Avoiding callback hell

I have this code that serves every markdown file in the './markdown' folder. At '/api/markdown/filename'.
var apiRouter = express.Router();
markdownFolder = './markdown/';
apiRouter.get('/:markdown_file_noext', function(req, res) {
fs.readdir(markdownFolder, function(err, markdown) {
if (err) throw err;
markdown.forEach(function(file) {
fs.readFile(markdownFolder + file, 'utf8', function(err, file_content) {
if (err) throw err;
fileNoExtension = file.slice(0, file.indexOf('.'));
if (req.params.markdown_file_noext == fileNoExtension) {
res.json({
'title': fileNoExtension,
'markdown': marked(file_content)
});
};
});
});
});
});
But i end having a ton of callbacks do the the nature of the 'fs' methods. How do i avoid this?
Using Q as promise library:
const Q = require('q');
const fs = require('fs');
const markdownFolder = './markdown/';
const readdir = Q.nfbind(fs.readdir);
const readFile = Q.nfbind(fs.readFile);
readdir(markdownFolder).then(markdown => {
const promises = [];
markdown.forEach(file => promises.push(readFile(markdownFolder + file, 'utf8')));
return Q.all(promises);
}).then(files => {
// Do your magic.
}).catch(error => {
// Do something with error.
});
You have different option.
Use named Function instead of anonymus functinos. It would make it a little bit more readable but you will still be using callbacks.
Use Promises, but you will need to use bluebird to wrap the fs module.
For a more advance option, you can use generators and Promises to make your code look more like a sync way. Take a look at co or bluebird.coroutine.
With Promises you could do like this:
const path = require('path');
var apiRouter = express.Router();
markdownFolder = './markdown/';
apiRouter.get('/:markdown_file_noext', function(req, res) {
readdir(markdownFolder)
.then((files) => {
const tasks = files.map((file) => {
const filePath = path.resolve(markdownFolder, file);
return readFile(filePath);
});
return Promise.all(tasks); // Read all files
})
.then((fileContents) => {
return fileContents.map((content) => {
fileNoExtension = file.slice(0, file.indexOf('.'));
if (req.params.markdown_file_noext == fileNoExtension) {
return {
'title': fileNoExtension,
'markdown': marked(content)
};
};
})
})
.then((results) => {
// It's better if you aggregate all results in one array and return it,
// instead of calling res.json for each result
res.json(results);
})
.catch((err) => {
// All errors are catched here
console.log(err);
})
});
function readdir(folderPath) {
return new Promise((resolve, reject) => {
fs.readdir(folderPath, (err, files) {
if (err) {
return reject(err);
}
resolve(files);
});
});
}
function readFile(filePath) {
return new Promise((resolve, reject) => {
fs.readFile(filePath, 'utf8', (err, file_content) => {
if (err) {
return reject(err);
}
resolve(file_content);
});
});
}

Bluebird - promisification - promisifyAll of 'email-templates' Node Module - Send mail

I am trying to convert email-templates node module into promise. I am using bluebird for promisification but it couldn't converted.
var emailTemplates = Promise.promisifyAll(require('email-templates'));
Is promisification node module supports this conversion or Am I doing any mistake?
EDITED :
I am doing like this now but wanna convert this to bluebird promise.
var emailTemplates = require('email-templates');
var path = require('path');
var templatesDir = path.resolve(__dirname, '../..', 'assets/templates');
var postmark = require('postmark');
var postmarkKey = MY_POSTMARK_KEY;
var postmarkClient = postmark(postmarkKey);
module.exports = {
sendEmail : function (templateName, locals, callback) {
emailTemplates(templatesDir, function (err, template) {
if (err)
return callback(err, null);
else {
template(templateName, locals, function (err, html, text) {
if (err) {
return callback(err, null);
}
else {
postmarkClient.send({
From: locals.from,
To: locals.to,
Subject: locals.subject,
HtmlBody: html
TextBody: text
}, function (err, responseStatus) {
if (err) {
return callback(err, null);
}
else {
return callback(err, responseStatus);
}
});
}
});
}
});
}
}
emailTemplates is a function, so you'd do:
var emailTemplates = Promise.promisify(require('email-templates'));
The problem is that it does not behave well since the function itself has a callback argument, so you'd have to do:
emailTemplates().then(function(template){
Promise.fromNode(template.bind(null, "template-name")).then(...
});
Promisify missbehaved for me as well, so i made a manual promisification.
var postmark = require("postmark");
var client = new postmark.Client("POSTMARK_API_TEST");
var Promise = require('bluebird');
exports.sendInviteEmail = function(email) {
let promise = new Promise((resolve, reject) => {
client.sendEmail({
"From": "donotreply#example.com",
"To": "target#example.us",
"Subject": "Test",
"TextBody": "Test Message"
}, function(error, result) {
if(error) {
reject(error);
} else {
resolve(result);
}
})
});
return promise;
}

Resources