socket.io functions not working in callbacks - node.js

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.

Related

Callback function gets called only once

I'm writing a ZooKeeper client to monitor a node. The callback function gets called only the first time I change the data of the node. I'm not sure why the function doesn't execute on the second change.
A second query is that my program terminates if I uncomment the last close() function. As a good practice, I should use the close() function but then it doesn't enter the blocking state to listen to the incoming events. How do I achieve it? I've read the documentation but couldn't find anything useful.
var zookeeper = require("node-zookeeper-client");
var client = zookeeper.createClient("192.168.43.172:2181");
var path = process.argv[2];
client.once("connected", function() {
console.log("Connected to the server.");
client.create(path, new Buffer("data"), function(error) {
if (error) {
console.log("Failed to create node: %s due to: %s.", path, error);
} else {
console.log("Node: %s is successfully created.", path);
}
});
client.getData(
path,
function(event) {
console.log("Got event: %s.", event);
},
function(error, data, stat) {
if (error) {
console.log(error.stack);
return;
}
console.log("Got data: %s", data.toString("utf8"));
}
);
//client.close();
});
client.connect();
I still don't have an answer to my second query but for the first query, the zookeeper server is designed to send an event only the first time a change takes place and then delete the switch. In order to keep receiving the events, I had to set a watch again while handling the triggered event. Below is my code:
var zookeeper = require("node-zookeeper-client");
var client = zookeeper.createClient("192.168.43.172:2181");
var path = process.argv[2];
client.once("connected", function() {
console.log("Connected to the server.");
var watch = function(event) {
console.log("Got event: %s.", event);
client.getData(path, watch, getDat);
};
var getDat = function(error, data, stat) {
if (error) {
console.log(error.stack);
return;
}
console.log("Got data: %s", data.toString("utf8"));
};
client.getData(path, watch, getDat);
// client.close();
});
client.connect();

callback function not working in node-scheduler

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

rest api using express.js and PostgreSQL

I have a script that has rest apis that gets data from a postgresql database and returns it back to the client. At the start, the script only uses the about 7mb of memory and the response time when making queries is very fast. However, as time passes by(about 1 day), the memory used by the script balloons to 170mb. And now, the queries takes more than 1 minute to respond. But when I restart the script, it is now again fast on its response. I am clueless as to why this happens. Can anybody shed light on this? Here is a portion of what my script looks like:
var port = process.env.PORT || 8000;
var router = express.Router();
router.get('/:id/from/:prevdate', function (req, res) {
var results = [];
var id = req.params.id;
var prevdate = req.params.prevdate;
pg.connect(connectionString, function (err, client, done) {
var query = client.query("some sql statement here", [id, prevdate]);
query.on('row', function (row) {
results.push(row);
});
query.on('end', function () {
client.end();
return res.json(results);
});
if (err) {
console.log(err);
}
});
});
router.get('/:id/getdata', function (req, res) {
var results = [];
var id = req.params.id;
pg.connect(connectionString, function (err, client, done) {
var query = client.query("some sql statement here", [id]);
query.on('row', function (row) {
results.push(row);
});
query.on('end', function () {
client.end();
return res.json(results);
});
if (err) {
console.log(err);
}
});
});
app.use('/restapitest', router);
app.listen(port);
console.log('Webservice started using port: ' + port);
You are mixing connection pooling (which uses done()) with creating single connections (which uses client.end()).
Try this:
query.on('end', function() {
done();
return res.json(results);
});
Also, since you are storing all results in memory anyway, there's no need to use events. So with proper error and connection handling, you could use this:
pg.connect(connectionString, function (err, client, done) {
var sendError = function(err) {
console.log(err);
return res.sendStatus(500);
};
if (err) return sendError(err);
client.query("some sql statement here", [id, prevdate], function(err, results) {
// Done with the client.
done();
// Handle any errors.
if (err) return sendError(err);
// Return result
return res.json(results);
});
});

Send db-query-result to client

Trying to send results from a node-mysql database query to the client.
Code:
io.on('connection', function(socket){
socket.on('admin', function() {
[...]
locations = getUserInfo(function(e,c){...});
[...]
});
});
function getUserInfo(callback) {
var json = '';
connection.query('SELECT * from locations WHERE loc_id = 1', function(err, results, fields) {
if(err)
return callback(err, null);
console.log('The query-result is: ', results[0]);
json = JSON.stringify(results);
console.log('JSON-result: ', json);
callback(null, json);
});
};
getUserInfo(function(e,c){
console.log(c);
});
This is working as expected.
But I don't want to write it to the console but send it to the client (with socket.io). How can I do this? All my attempts ended in getting undefined as result.
You might be sending the locations object back, which is always undefined. Try:
io.on('connection', function(socket){
socket.on('admin', function() {
getUserInfo(function(e,c){
if(!e){
socket.emit('locations', c);
}
});
});
});

Send update notification to particular users using socket.io

Following is the code on front end, where storeSelUserId contains user_id to send the message-
FYI - Node Version 1.1.0
// Socket Notification
var socket = io('http://localhost:6868');
socket.on('connection', function (data) {
socket.emit('send notification', { sent_to: storeSelUserId });
});
Following is the server code in routes file -
var clients = {};
io.on('connection', function (socket) {
socket.emit('connection', "Connection Created.");
socket.on('send notification', function (sent_to) {
console.log(sent_to);
});
});
In console sent_to is showing the array of user_id.
Now being a starter in socket.io I stuck with the solution that how do I send the message to these particular userids.
I search and found that I need to push each user with its sockets so I reformed it to -
var users = [];
io.on('connection', function (socket) {
users.push({socket_id: socket.id});
socket.emit('connection', "Connection Created.");
socket.on('send notification', function (sent_to) {
console.log(sent_to);
});
});
But I am in dilemma that else do I need to do to store which user_id refers to which socket_id and then update the div of users with that particular ids?
EDIT -
Add Controller - (Front End)
Front end Interface where memo is created and send to particular users
var socket = io('http://localhost:6868');
socket.on('connection', function (data) {
socket.emit('send memo notification', {creator_id: creator_id, sent_to: [Array of user_ids to whom memo to send]});
});
Dashboard controller - (Front End)
Front end Interface where notification count to show "notificationCount"
if (SessionService.currentUser._id) {
var socket = io('http://localhost:6868');
socket.on('connection', function (data) {
socket.emit('get notifications', {user_id: SessionService.currentUser._id});
});
socket.on('notification data', function(data){
console.log("-- Not Data Test -");
$scope.notificationCount = data.length;
});
}
Code at server end -
io.on('connection', function (socket) {
socket.emit('connection', "Connection Created.");
socket.on('send memo notification', function(data) {
notifications.createNotification(data);
});
socket.on('get notifications', function(data){
notifications.getNotifications(data, function(response){
socket.emit('notification data', response.data);
});
});
});
Backend controller code -
exports.getNotifications = function(data, callback) {
var userId = data.user_id;
Notification.find({receiver_id: userId}, function(err, response){
if (err)
callback({"message": "error", "data": err, "status_code": "500"});
else
callback({"message": "success", "data": response, "status_code": "200"});
});
};
exports.createNotification = function(data) {
var notificationData = data;
var x = 0;
for(var i=0; i< notificationData.length; i++) {
// Code
Notification(notificationData[i]).save(function(err,response){
if (err)
return false;
});
if (x === notificationData.length - 1) {
return true;
}
x++;
}
};
If you want to use your own user ids then there is no way around mapping the socket id to the user id. I assume a client knows its user id from somewhere, so it could send its user id to the server after connection.
Client
socket.on('connection', function (data) {
socket.emit('setUserId', myUserId);
});
The server saves the socket for each user id.
socket.on('setUserId', function (userId) {
users[userId]=socket;
});
If you have such a mapping in the server you can send a message just to this client using the user id.
socket.on('send notification', function (userId) {
users[userId].emit('notification', "important notification message");
});
Edit: Saving the corresponding socket directly is even better.
According to what i understand, you need private notification send to only some users. For that, save your users name to whom you want to send and their corresponding socket in different hashes.
username [socket.name] = username to be added;
usersocket [ socket.name ] =socket;
Then to emit the messages to that user only, use
usersocket[ socket.name ].emit('event for send message', ' what you want to send ');

Resources