callback function not working in node-scheduler - node.js

I have successfully written a code to read unseen emails in my email box using node.js. it works fine when I run it in the normal javascript file. now I want to schedule the script to run every 30 seconds. so wrote a code snippet using node-scheduler as below. running this I just get reading unread emails....### as output, console.log(msg); not print anything. if I run this without nod-scheduler it works fine. what might be the problem? I tried await keyword with email.unreadEmail function, it didn't work too.
I believe that this is something happening because of asynchronous behavior. if so is there way to convert this code to promises? I am little bit confiuse because there are multiple nexted callback in imap interface.
schedule.scheduleJob('*/30 * * * * *', () => {
logger.info(`scheduler runs at ${new Date()} for check unread emails`);
let email = new mail.default();
email.unreadEmail((msg) => {
console.log(msg);
});
});
here is the code for email reading
unreadEmail(callback){
logger.info(`reading unread emails....###`);
imap.once('ready', () => {
openIncidents((err, box) => {
if (err) throw err;
imap.search([ 'UNSEEN'], (err, results) => {
if (err) throw err;
try{
// var f = imap.fetch(results, { bodies: '',markSeen: true });
var f = imap.fetch(results, { bodies: '' });
f.on('message', (msg, seqno) => {
console.log('Message #%d', seqno);
var prefix = '(#' + seqno + ') ';
msg.on('body', (stream, info) => {
simpleParser(stream, (err, mail) => {
callback(mail.subject);
});
// stream.pipe(fs.createWriteStream('msg-' + seqno + '-body.txt'));
});
msg.once('attributes', function(attrs) {
logger.info(prefix + 'Attributes: %s', inspect(attrs, false, 8));
});
msg.once('end', function() {
logger.info(prefix + 'Finished');
});
});
f.once('error', function(err) {
logger.error('Fetch error: ' + err);
});
f.once('end', function() {
logger.info('Done fetching all messages!');
imap.end();
});
}catch (e) {
logger.error('Error from fetching mails, probably there are no unseen emails in mailbox');
}
});
});
});
logger.info(`end of the email function....###`);
}
Updated
this behavior is same with setInterval function
setInterval(()=>{
email.unreadEmail((msg)=>{...})}, 30000)

Assuming you're using the imap NPM module (you didn't specify in the original question), the 'ready' event for the new imap connection only fires one time upon authenticating (see code for _login here: https://github.com/mscdex/node-imap/blob/master/lib/Connection.js#L1604).
Therefore, instead of putting imap.once('ready' ... within the schedule, you should put your schedule inside of the imap.once('ready' ... callback. I would expect something like the following to work making sure that you remove imap.once('ready', () => { ... from the unreadEmail function. Also, based on your use case setInterval is probably a better choice as opposed to node-schedule:
imap.once('ready', () => {
setInterval(() => {
logger.info(`scheduler runs at ${new Date()} for check unread emails`);
let email = new mail.default();
email.unreadEmail((msg) => {
console.log(msg);
});
}, 30000);
});

Related

Check Mailbox in a Loop, Read Email and Create PDF

I have a code that reads unseen emails and creates pdf.
The problem is;
I cannot pull email if any new unseen email exist without executing code again.
var Imap = require('imap');
const MailParser = require('mailparser').MailParser;
var pdf = require('html-pdf');
var fs = require('fs');
var Promise = require("bluebird");
Promise.longStackTraces();
var imapConfig = {
user: '*****',
password: '*****',
host: 'imap.gmail.com',
port: 993,
tls: true
};
var imap = new Imap(imapConfig);
Promise.promisifyAll(imap);
imap.once("ready", execute);
imap.once("error", function(err) {
log.error("Connection error: " + err.stack);
});
imap.connect();
function execute() {
imap.openBox("INBOX", false, function(err, mailBox) {
if (err) {
console.error(err);
return;
}
imap.search(["UNSEEN"], function(err, results) {
if(!results || !results.length){console.log("No unread mails");imap.end();return;}
var f = imap.fetch(results, { bodies: "" });
f.on("message", processMessage);
f.once("error", function(err) {
return Promise.reject(err);
});
f.once("end", function() {
console.log("Done fetching all unseen messages.");
imap.end();
});
});
});
}
const options = { format: 'A2', width:"19in", height:"17in", orientation: "portrait" };
function processMessage(msg, seqno) {
console.log("Processing msg #" + seqno);
// console.log(msg);
var parser = new MailParser();
parser.on("headers", function(headers) {
console.log("Header: " + JSON.stringify(headers));
});
parser.on('data', data => {
if (data.type === 'text') {
console.log(seqno);
console.log(data.html); /* data.html*/
var test = data.html
pdf.create(test, options).toStream(function(err, stream){
stream.pipe(fs.createWriteStream('./foo.pdf'));
});
}
});
msg.on("body", function(stream) {
stream.on("data", function(chunk) {
parser.write(chunk.toString("utf8"));
});
});
msg.once("end", function() {
// console.log("Finished msg #" + seqno);
parser.end();
});
}
Also I have tried to use setInterval to check new unseen emails but I get
'Error: Not authenticated'
How can I pull new unseen emails in a loop and create pdf from that email?
Your observation is correct. You must poll your IMAP (or POP3) server on a regular schedule to keep up with incoming messages. Depending on your requirements, a schedule of once every few minutes is good.
continous polling, or polling every second or so, is very rude. The operator of the IMAP server may block your application if you try to do that: it looks to them like an attempt to overload the server.

Nodejs + redis + displaying results on the browser

I am having trouble displaying the results that I retrieve from redis with the redis driver in nodejs.
I display an array that initially was a JSON, but it displays lots of "/n" between the elements and properties and I have spent hours trying to fix it but I did not succeed.
Any hint about how can I present the information in the browser without the "/n" characters?
JSON.stringify? I have try it and no way. util.inspect... I have try it. I know I am doing something wrong but I cannot find what.
My code goes like this:
app.get('/retrieve_from_redis_promise', function(req, res) {
client.on('connect', function() {
console.log('Connected to Redis succesfully');
});
var p = new Promise(function(res, reject) {
if (true) {
client.hgetall("redis_db_name", function(err, replies) {
res(replies)
// Object.keys(replies).forEach(key => {});
});
} else {
reject(Error("It broke"));
}
});
p.then(function(result) {
res.send(result); // "Stuff worked!"
}, function(err) {
console.log(err); // Error: "It broke"
});
});
Try
client.hgetall("redis_db_name", function(err, replies) {
Object.keys(replies).forEach(key => {
replies[key] = JSON.parese(replies[key]);
});
res(replies)
});

Socket io server freezing(it seems) after certain amount of time?

My code is too long to post in here, but basically I use socket.io server to pull data from database and refresh in the client every 1 second.
Like this:
function updateTimer(){
//db->query
io.sockets.emit('updated data', data);
}
setInterval(updateTimer, 1000);
After a certain amount of time, the server just stops emitting data. I use a chat on the website as well and it stops too. But in the server console nothing is shown, no errors or any outputs, just "listening on port 3000..." stays on the screen all the time.
I thought it could be the loop maybe but I think there's no other way of refreshing data every 1 second, am I right?
If someone can help me and needs the full code please open a discussion and I'll paste it somewhere.
EDIT for the function code:
function checkRoulleteTime() {
try {
pool.getConnection(function(err, connection) {
connection.query('SELECT * FROM `roullete` WHERE status=\'active\'', function(err, rows) {
if (err) {
console.log(err);
return;
}
if (rows.length == 0) return;
var time = rows[0].time - (Math.floor(Date.now() / 1000));
if (time <= 1) {
connection.query('UPDATE `roullete` SET `status`=\'closed\' WHERE `id`=\'' + rows[0].id + '\'', function(error, fields) {
if (error) throw error;
});
setTimeout(roll, 1000);
setTimeout(function(){
io.sockets.emit('add hist');
}, 10500);
setTimeout(updatePoints, 12000);
setTimeout(newRound, 12500);
}
var contagem = Object.keys(clients).length;
io.sockets.emit('login count', contagem);
io.sockets.emit('roullete time', time);
connection.query('SELECT SUM(points) as points FROM `roullete_bets` WHERE `round`=\'' + rows[0].id + '\' AND `color`=\'black\'',function(error2, rows2){
if (error2) throw error2;
connection.query('SELECT SUM(points) as points FROM `roullete_bets` WHERE `round`=\'' + rows[0].id + '\' AND `color`=\'green\'',function(error4, rows4){
if (error4) throw error4;
connection.query('SELECT SUM(points) as points FROM `roullete_bets` WHERE `round`=\'' + rows[0].id + '\' AND `color`=\'red\'',function(error3, rows3){
if (error3) throw error3;
var onBlack = rows2[0].points;
var onRed = rows3[0].points;
var onGreen = rows4[0].points;
io.sockets.emit('calculations', {"time": time, "black" : onBlack, "red" : onRed, "green" : onGreen});
});
});
});
});
connection.release();
});
} catch(e) {
console.log('error here:'+e);
}
setTimeout(checkRoulleteTime, 1000);
}
setTimeout(checkRoulleteTime, 1000);
Alright, that's my code.
My guess is that you have some sort of programming error in your database query that, after a little while exhausts some resources so your database query starts failing every time, throwing an exception or just returning an error and thus you don't ever send any data because of the error. Because the code is inside the setInterval() callback, any exception is not logged for you.
We could probably help you further if you included your actual database code, but you can start to debug it yourself by putting an exception handler around it like this:
function updateTimer(){
try {
//db->query
io.sockets.emit('updated data', data);
} catch(e) {
console.log(e);
}
}
setInterval(updateTimer, 1000);
And, if your DB query is async (which I'm assuming it is), you will need to have explicit error handling and an exception at each callback level (since exceptions don't propagate up async callbacks).
If your database may get slow, then it might be safer to change your recurring code to work like this:
function updateTimer(){
try {
//db->query
io.sockets.emit('updated data', data);
} catch(e) {
console.log(e);
}
// schedule next updateTimer() call when this one has finished
setTimeout(updateTimer, 1000);
}
// schedule first updateTimer() call
setTimeout(updateTimer, 1000);
You have LOTS of places in your code where you are leaking a pooled connection and lots of places where you are not logging an error. My guess is that you are running out of pooled connections, getting an error over and over that you don't log.
Here's a version of your code that attempts to clean things up so all errors are logged and no pooled connections are leaked. Personally, I would write this code using promises which makes robust error handling and reporting a ton easier. But, here's a modified version of your code:
function checkRoulleteTime() {
try {
pool.getConnection(function (err, connection) {
if (err) {
console.log("Failed on pool.getConnection()", err);
return;
}
connection.query('SELECT * FROM `roullete` WHERE status=\'active\'', function (err, rows) {
if (err) {
connection.release();
console.log(err);
return;
}
if (rows.length == 0) {
connection.release();
return;
}
var time = rows[0].time - (Math.floor(Date.now() / 1000));
if (time <= 1) {
connection.query('UPDATE `roullete` SET `status`=\'closed\' WHERE `id`=\'' + rows[0].id + '\'', function (error, fields) {
if (error) {
console.log(error);
connection.release();
return;
}
});
setTimeout(roll, 1000);
setTimeout(function () {
io.sockets.emit('add hist');
}, 10500);
setTimeout(updatePoints, 12000);
setTimeout(newRound, 12500);
}
var contagem = Object.keys(clients).length;
io.sockets.emit('login count', contagem);
io.sockets.emit('roullete time', time);
connection.query('SELECT SUM(points) as points FROM `roullete_bets` WHERE `round`=\'' + rows[0].id + '\' AND `color`=\'black\'', function (error2, rows2) {
if (error2) {
console.log(error2);
connection.release();
return;
}
connection.query('SELECT SUM(points) as points FROM `roullete_bets` WHERE `round`=\'' + rows[0].id + '\' AND `color`=\'green\', function (error4, rows4) {
if (error4) {
console.log(error4);
connection.release();
return;
}
connection.query('SELECT SUM(points) as points FROM `roullete_bets` WHERE `round`=\'' + rows[0].id + '\' AND `color`=\'red\'', function (error3, rows3) {
connection.release();
if (error3) {
console.log(error3);
return;
}
var onBlack = rows2[0].points;
var onRed = rows3[0].points;
var onGreen = rows4[0].points;
io.sockets.emit('calculations', {
"time": time,
"black": onBlack,
"red": onRed,
"green": onGreen
});
});
});
});
});
});
} catch (e) {
console.log('error here:' + e);
}
setTimeout(checkRoulleteTime, 1000);
}
setTimeout(checkRoulleteTime, 1000);
And to give you an idea how much simpler it can be to do error handling and chaining of sequential or dependent async functions, here's your function rewritten using promises. I have no idea if this runs without error since I have no way of testing it, but it should give you an idea how much cleaner programming with promises can be:
var Promise = require('bluebird');
pool = Promise.promisifyAll(pool);
function logErr(err) {
console.log(err);
}
function checkRoulleteTime() {
pool.getConnectionAsync().then(function(connection) {
var query = Promise.promisify(connection.query, {context: connection});
return query('SELECT * FROM `roullete` WHERE status=\'active\'').then(function(rows) {
if (rows.length !== 0) {
var time = rows[0].time - (Math.floor(Date.now() / 1000));
if (time <= 1) {
query('UPDATE `roullete` SET `status`=\'closed\' WHERE `id`=\'' + rows[0].id + '\'').catch(logErr);
setTimeout(roll, 1000);
setTimeout(function () {
io.sockets.emit('add hist');
}, 10500);
setTimeout(updatePoints, 12000);
setTimeout(newRound, 12500);
}
var contagem = Object.keys(clients).length;
io.sockets.emit('login count', contagem);
io.sockets.emit('roullete time', time);
function doQuery(color) {
return query('SELECT SUM(points) as points FROM `roullete_bets` WHERE `round`=\'' + rows[0].id + '\' AND `color`=\'' + color + '\'');
}
return Promise.all([doQuery('black'), doQuery('green'), doQuery('red')]).then(function(results) {
io.sockets.emit('calculations', {
"time": time,
"black": results[0][0].points,
"green": results[1][0].points,
"red": results[2][0].points
});
});
}
}).catch(logErr).finally(function() {
connection.release();
setTimeout(checkRoulleteTime, 1000);
});
}, function(err) {
console.log("Err getting connection: ", err);
});
}

socket.io functions not working in callbacks

The issue I am having is that I want the current session to join a room if they pass a database check. If they do pass the check, meaning the query returns a result, it should add the user to a room. However, when I call socket.join("room"), it does not work if its in the database callback function. Here is a SIMPLIFIED version of my code:
var pg = require('pg');
var conString = 'connection_to_postgres_server'
var server = require('http').createServer();
var io = require('socket.io')(server);
var port = 8080;
function defaultOnSuccess(query, result){
console.log("Result of query `", query, "` -> ", result);
}
function defaultOnError(query, err){
console.log("Error of query `", query, "` -> ", err);
}
function runQuery(query, queryParams, onSuccess, onError){
onSuccess = (typeof onSuccess !== 'undefined' ? onSuccess : defaultOnSuccess);
onError = (typeof onError !== 'undefined' ? onError : defaultOnError);
pg.connect(conString, function(err, client, done){
if(err){
onError(query, err);
done();
return;
}
client.query(query, queryParams, function(err, result){
if(err){
onError(query, err);
done();
return;
}
else {
onSuccess(query, result);
done();
return;
}
});
});
}
function listenOn(channel, onNotification, onError){
onError = (typeof onError !== 'undefined' ? onError : defaultOnError);
pg.connect(conString, function(err, client, done){
if(err){
onError(channel, err);
done();
return;
}
client.on('notification', function(msg) {
onNotification(channel, msg);
});
var query = client.query("LISTEN \"" + channel + "\";");
done();
});
}
io.on('connection', function(socket){
runQuery("THIS QUERY SHOULD RETURN EXACTLY ONE RESULT IF THE USER IS VALIDATED", [],
function(query, result){
if(result.rowCount == 1){
console.log("Pre rooms: ", socket.rooms);
socket.join("hello");
console.log("Current rooms: ", socket.rooms);
}
}
);
});
io.on('connection', function(socket){
socket.on('disconnect', function(){
});
});
server.listen(port, function(){
console.log('listening on *:' + port);
listenOn("project_tc_changed",
function(channel, message){
console.log(message);
io.emit("data", message);
}
);
});
When I connect with a client, the output of the "Pre rooms:" and "Current rooms:" log is exactly the same. In addition, the io.emit() in server.listen does not work, even though I know the code is getting called, because the message gets logged.
I know for a fact that the socket.join() call and the io.emit() call are getting reached, they are just not having any effects, and not returning any errors.
The socket.join is working as expected, but due to the asynchronous nature of javascript your console logs are not showing what you expected. In javascript every line of code is ran asynchronously, including your socket.join and console.log. This is why we have to make use of callbacks to see what the environment looks like after a function has completed. socket.join allows for this callback. So to see the room join in action, we simply have to change our 3 lines of code to the following:
console.log("Pre rooms: ", socket.rooms);
socket.join("hello", function(){
console.log("Current rooms: ", socket.rooms);
);
As for your emit; If you believe your emit is being reached and the message variable contains data, your io.emit should work. So without seeing what the client side code looks like it is hard to help solve for this.

Can't make Mocha before() nested callback clause behave synchronously

Running into async hell again with the following inner callback in a test I am writing. I've commented the callback that doesn't wait. I'm using both an async.series to marshall the functions, and async.each to keep the inner iteration synchronous. Mocha compalins "done() was called multiple times" - why isn't the code waiting?
describe('Report Generation (R subsystem)', function () {
before(function (done) {
//clear test files
async.series([function (callback) { //1st
console.log('Delete local test files');
_.each(output_file_template, function (test_file) {
if (fs.existsSync(__dirname + '/../reports/' + test_file + user_file_code + '.png')) {
fs.unlinkSync(__dirname + '/../reports/' + test_file + user_file_code + '.png');
};
}); //..._.each
callback();
}, function (callback) { //2nd
console.log('Delete remote test files');
async.each(output_file_template, function (test_file, cb) {
console.log(test_file);
s3.del('reports/' + test_file + user_file_code + '.png', function (err, res) {
console.log("Delete err", err);
console.log("Delete result", res);
cb();
}, function(err) {callback(err);}); //s3.head
}); //...async.each
}], function (err, res) { //3rd
done(); //this should tell the begin() clause to complete
}); //...async.series
}); //...before
it('should not start this test until before() has finished!', function (done) {
console.log("1st test here");
});
});
, What I can see is, you are doing async.series with an array of 3 functions, but no controlling function at the end.
I assume, your code in it('should not start... is the one which should be in there.
So (I think) your code should look like this:
describe('My Test 1', function () {
//clear test files
async.series([
function (callback) { //1st
console.log('Delete local test files');
...
callback();
},
function (callback) { //2nd
console.log('Delete remote test files');
async.each(
output_file_template,
function (test_file, cb) {
console.log(test_file);
s3.del('reports/' + test_file + user_file_code + '.png', function (err, res) { //I can't get the following nested callback to wait
console.log("Delete err", err);
console.log("Delete result", res);
cb();
}); //...s3.head
},
function( err ) { // this is the control function for async.each, so now it waits after all s3 have finished
callback( err );
}
); //...s3.head
},
function (callback) { //3rd -> will be called now after the async.each (I don't think, you use it so can be deleted anyway)
callback();
}
],
function( err, result ) {
done(); // whatever this has to do with the last "it" -> here is the point, where the "before" is completely done
}
});
I didn't test the source, so maybe there are typos inside, but I think it shows the picture.

Resources