Can't make works socket.io with my handshake - node.js

I don't understand where is my problem.
When I put this:
io = require('socket.io').listen(server).of("/");
I my handshake looks like running fine, but all my socket.io's event (like connection) never works.
And when I when i put this
io = require('socket.io').listen(server);
I got this :
TypeError: Object # has no method 'authorization'
If I remove my handshake, all the socket.io's will works, but i need it :/
Here my app.js
const express = require('express')
, app = express()
, http = require('http')
, ejs = require('ejs')
, server = http.createServer(app)
, port = process.env.PORT || 1337
, io = require('socket.io').listen(server).of("/")
;
module.exports = { app: app, server: server };
// Grab "cookie" and "connect" from express
var connect = require('express/node_modules/connect')
var cookie = require('express/node_modules/cookie')
var connectes =[];
app.configure(function(){
this.use("/public", express.static(__dirname + '/public'));
this.use(express.cookieParser());
this.use(express.json());
this.use(express.urlencoded());
this.sessionStore = new express.session.MemoryStore({ reapInterval: 60000 * 10 });
this.use(express.session({
"secret": "some private string",
"store": this.sessionStore
}));
});
app.engine('html', ejs.renderFile);
app.set('views', __dirname + '/views');
app.get('/', [requireLogin], function(request, response) {
var pseudo = request.session.pseudo;
response.render('index.ejs.html',{connectes:connectes});
});
app.get('/login', function(request, response) {
response.render('login.ejs.html');
});
app.post("/login", function(req, res){
if (req.param("user") != "") {
req.session.pseudo = req.param("user");
//socket.set('pseudo',req.param("user"));
connectes.push(req.param("user"));
res.render('index.ejs.html',{connectes:connectes});
}else{
res.render('login.ejs.html');
}
});
const parseCookie = require('connect').utils.parseCookie;
io.authorization(function (handshakeData, callback) {
var cookies = cookie.parse(handshakeData.headers.cookie);
console.log(cookie);
});
io.on('connection', function(socket){
console.log("This never appear in log :/");
socket.on('login',function(user,callback){
var pseudoValide = true;
for (var i = 0; i < connectes.length; i++) {
if(connectes[i]==user.pseudo){
pseudoValide = false;
callback(true);
}
}
if (pseudoValide) {
socket.set('pseudo',user.pseudo);
pseudo = user.pseudo;
socket.broadcast.emit('newuser', pseudo);
socket.broadcast.emit('nvluseronline', pseudo);
connectes.push(pseudo);
callback(false,pseudo);
}
});
socket.on('message',function(message){
messageUser = message.message;
socket.get('pseudo',function(error, pseudo){
io.sockets.emit('nouveauMessage',{pseudo:pseudo,message:messageUser});
})
});
socket.on('disconnect',function(){
socket.get('pseudo',function(error, pseudo){
connectes = unset(connectes, pseudo);
io.sockets.emit('nvldisc', pseudo);
})
});
});
function unset(array, value){
var output=[];
var index = array.indexOf(value)
{
var j = 0;
for(var i in array)
{
if (i!=index)
{
output[j]=array[i];
j++;
}
}
return output;
}
}
/** Middleware */
function requireLogin (req, res, next) {
if (req.session.pseudo) {
console.log(req.session.pseudo);
next();
}else{
res.redirect("/login");
}
}
if (!module.parent) {
server.listen(port, function () {
console.log('Listening', this.address());
})
}
Like you can see i'm a beginner lost !
Thank you guys !

You should read the documentation on authorization carefully. authorization method only exists for namespaces. When doing global authorization, you have to do:
io.configure(function (){
io.set('authorization', function (handshakeData, callback) {
callback(null, true); // error first callback style
});
});
It'll work fine if you have .of("/") as server endpoint, but it is a namespace. Without namespaces you would have to use above method.

Related

Nodejs How to export Socket IO in controller

In Nodejs how to export socket IO in controller.
Socket.io Version - "socket.io": "^4.5.1",
Socket.js
let io = null;
// module.exports = {
// intialized_connection: (httpServer) => {
// return (io = require('socket.io')(httpServer, {
// cors: {
// origin: '*',
// methods: ['GET', 'POST', 'PUT', 'DELETE'],
// },
// }));
// },
// getIO: () => {
// if (!io) {
// throw new Error('Socket.io is not initialized');
// }
// return io;
// }
// }
class RealTime {
constructor() {
if (io) return io;
io = this;
return io;
}
intialized_connection(httpServer) {
return (io = require('socket.io')(httpServer, {
cors: {
origin: '*',
methods: ['GET', 'POST', 'PUT', 'DELETE'],
},
}));
}
init() {
io.on("connection", function (socket) {
console.log("A user connected", socket.id);
//Whenever someone disconnects this piece of code executed
// socket.on('custom-event', function(data) {
// console.log("Atique data: ", JSON.stringify(data));
// });
// socket.emit('custom-emit', "hello from nodejs")
socket.on('disconnect', function () {
console.log('A user disconnected');
});
});
}
getIO() {
if (!io) {
throw new Error('Socket.io is not initialized');
}
return io;
}
sendEvents(event, data) {
console.log("This.Socket:", this.socket);
return new Promise((resolve, reject) => {
this.getIO().emit(event, data, (response) => {
if (response.error) {
console.error(response.error);
reject(response.error);
} else {
resolve(true);
}
});
});
}
receivedEvents(event) {
console.log("Atique Ahmed Received Events ---->", event);
return new Promise((resolve, reject) => {
this.getIO().on(event, function(err, data) {
console.log("I am emiiting here, ")
if(err) {
reject(err);
}
resolve(data);
});
})
}
}
module.exports = {
RealTime
};
index.js
const express = require('express');
const bodyparser = require('body-parser');
const cors = require('cors');
const fileUpload = require('express-fileupload');
const http = require('http');
// const socketIO = require('./utils/socket');
const { RealTime } = require('./utils/socket');
const socket = new RealTime();
const app = express();
app.use(cors())
app.options('*', cors());
app.use(bodyparser.json({limit: '5mb', extended: true}))
app.use(bodyparser.urlencoded({limit: '5mb', extended: true}))
const authRoutes = require('./routes/authRoutes');
const apiRoutes = require('./routes/routes');
// For File Upload
app.use(fileUpload({
limits: { fileSize: 5 * 1024 * 1024 },
}));
app.use('/auth', authRoutes);
app.use('/user', apiRoutes);
//Capture All 404 errors
app.use(function (req,res,next){
res.status(404).send('Error - Unable to find the requested resource!');
});
app.use((req, res, next) => {
req.socket.on('error', () => {});
next();
});
const server = http.createServer(app);
socket.intialized_connection(server);
socket.init();
app.set('socketio', socket);//here you export my socket.io to a global
module.exports = server;
local.js
require('dotenv').config()
const server = require('./index');
const port = process.env.PORT || 8081;
const chalk = require('chalk');
// Server
server.listen(port, () => {
console.log(chalk.green('╔═══════════════════════════════════════════════════════════'));
console.log(chalk.green('║ Background Server Listening at | port: %s', port));
console.log(chalk.green('╚═══════════════════════════════════════════════════════════'));
});
Routes.js
const express = require('express');
const bodyParser = require('body-parser');
const app = express();
app.use(bodyParser.json());
// Authentication
const authentication = require('../authentication');
// Middleware
const middleware = require('../middleware/headerValidation');
// Permission
const permissions = require('../permission/index')
// Controller
const userController = require('../controllers/userController');
const customerController = require('../controllers/customerController');
app.post('/submit-request', [middleware.bearerTokenPresent, authentication.verifyToken, permissions.fileUploadPermission], (req, res) => {
customerController.submitRequest(req, res);
});
module.exports = app;
customerController.js
exports.submitRequest = async(req, res) => {
const io = req.app.get('socketio');
io.emit('custom-emit', "Hello from nodejs");
io.on('custom-event', function(data) {
console.log("Atique:", JSON.stringify(data))
})
res.json("done")
}
Issue are -
The main issues are - socketio.on an socketio.emit is not working. It is working when I am putting everything, inside init method, I can't do that, I have to write the generic code, so it can be re-usable. -
init() {
io.on("connection", function (socket) {
console.log("A user connected", socket.id);
socket.on('custom-event', function(data) {
console.log("Atique data: ", JSON.stringify(data));
});
socket.emit('custom-emit', "hello from nodejs")
socket.on('disconnect', function () {
console.log('A user disconnected');
});
});
}
Frontend Angular 14, I am using, socket.io-client
Any idea, What I am doing wrong?
The object stored in your globals
app.set('socketio', socket);//here you export my socket.io to a global
is an instance of RealTime class and not of the require('socket.io').Server class.
please cache the reference to the proper object like so:
app.set('socketio',socket.intialized_connection(server));//here you export my socket.io to a global
socket.init();
change customerController.js:
exports.submitRequest = async (req, res) => {
const io = req.app.get('socketio');
///edited from io.on("connection", function (socket) {
io.once("connection", function (socket) {
socket.emit('custom-emit', "Hello from nodejs");
socket.on('custom-event', function (data) {
console.log("Atique:", JSON.stringify(data))
})
res.json("done")
});
}
you however have to keep in mind what listeners you are adding to the io object's "connection" or some other event as No checks are made to see if the listener has already been added. Multiple calls passing the same combination of "connection" and listener will result in the listener being added, and called, multiple times.
Consider using named functions and clearing the listener using removeListener() from time to time.
It is best to keep all your socket event listeners in one file for ease of debugging.
EDIT 1
index.js:
const server = http.createServer(app);
socket.intialized_connection(server);
socket.init();
app.set('socketio', socket);//here you export my socket.io to a global
make the following changes to your socket.js:
let io = null;
/// CHANGE:
let socketID = null;
class RealTime {
constructor() {
if (io) return io;
io = this;
return io;
}
intialized_connection(httpServer) {
return (io = require('socket.io')(httpServer, {
cors: {
origin: '*',
methods: ['GET', 'POST', 'PUT', 'DELETE'],
},
}));
}
init() {
io.on("connection", function (socket) {
console.log("A user connected", socket.id);
/// CHANGE:
socketID = socket.id
//Whenever someone disconnects this piece of code executed
// socket.on('custom-event', function(data) {
// console.log("Atique data: ", JSON.stringify(data));
// });
// socket.emit('custom-emit', "hello from nodejs")
socket.on('disconnect', function () {
console.log('A user disconnected');
});
});
}
/// CHANGE: getIO() {
getSocket() {
if (!io) {
throw new Error('Socket.io is not initialized');
}
return io.sockets.sockets.get(socketID);
}
.
.
.
.
for the receivedEvents and sendEvents to get reference to the socket.
Since you are caching the socket id here, this code will work only for one client properly.
change customerController.js:
exports.submitRequest = async (req, res) => {
const socket = req.app.get('socketio').getSocket();
socket.emit('custom-emit', "Hello from nodejs");
socket.on('custom-event', function (data) {
console.log("Atique:", JSON.stringify(data))
})
res.json("done")
}

Do variables reset to their initial values after every post method in Node.js?

//jshint esversion:6
var logger;
var logName;
var pollName;
var names = [];
const fs = require("fs");
const express = require("express");
const bodyParser = require("body-parser");
const readline = require('readline');
const fileName1 = __dirname + "/public/logger.json";
const fileName2 = __dirname + "/public/poll.json";
const dataWriter1 = require(fileName1);
const dataWriter2 = require(fileName2);
const app = express();
app.use(bodyParser.urlencoded({
extended: true
}));
var reader = readline.createInterface({
input: fs.createReadStream(__dirname + '/public/names.txt'),
console: false
});
reader.on('line', function(line) {
names.push(line);
});
app.use(express.static(__dirname + '/public'));
app.get("/", function(req, res) {
res.sendFile(__dirname + "/index.html");
});
app.get("/route", function(req, res)
{
fs.readFile(fileName1,'utf-8',(err,data)=>
{
res.set('Content-Type', 'application/json');
res.send(data);
});
});
app.post("/", function(req, res)
{
if(req.body.button==1)
{
logName = req.body.name;
console.log(logName);
for (i = 0; i < names.length; i++)
{
if (logName == names[i])
{
logger=true;
break;
}
}
if(logger!=true)
{
logger=false;
}
dataWriter1.logger = logger;
fs.writeFile(fileName1, JSON.stringify(dataWriter1), function writeJSON(err)
{
if (err) return console.log(err);
console.log(JSON.stringify(dataWriter1));
});
logger=false;
}
else if(req.body.button==2)
{
console.log(logName);
pollName=req.body.option;
for(i=0;i<dataWriter2.list.length;i++)
{
if(dataWriter2.list[i].name==pollName)
{
dataWriter2.list[i].votes++;
dataWriter2.list[i].voter_name.push(logName);
//logName is undefined
}
}
fs.writeFile(fileName2, JSON.stringify(dataWriter2), function writeJSON(err)
{
if (err) return console.log(err);
console.log(JSON.stringify(dataWriter2));
});
}
res.redirect("/");
});
app.listen(3000, function()
{
console.log("Server Started!");
});
This is my server code in node.js. I have 2 forms in index.html and both have submit buttons with different value to differentiate. The first form has to be submitted to view the second form. My problem is that even though I'm storing a data from the first form in a variable it resets when I submit the second form even though when the server is still running.
I cannot understand why logName becomes undefined while I'm posting with the second submit button and is there any workaround?
I figured it out.. The server was restarting due to changes in a JSON file as I was running it by nodemon. Added JSON file to ignore list and now it's all fine.

MEAN API using node-restful no response

I'm trying to setup API for multiple projects that use same database structure (running on same CMS) but when I'm trying to reach some data I get no response.
index.js
var express = require("express");
var cors = require("cors");
var mongoose = require("mongoose");
var bodyParser = require("body-parser");
var methodOverride = require("method-override");
var _ = require("lodash");
var passport = require("passport");
var dbconfig = require("./app/config/database");
// Create the application
var app = express();
var user = require("./app/routes/User");
app.use(bodyParser.urlencoded({ extended: true }));
app.use(bodyParser.json());
app.use(methodOverride('X-HTTP-Method-Override'));
var connections = [];
// Auto CORS
app.use(cors());
/* Mnual CORS Start
app.use(function(req, res, next){
res.header("Access-Controll-Allow-Origin", "*");
res.header("Access-Controll-Allow-Methods", "GET,PUT,POST,DELETE");
res.header("Access-Controll-Allow-Headers", "Content-Type");
next();
});
/* Manual Cords end */
// Passport Middleware
app.use(passport.initialize());
app.use(passport.session());
require("./app/config/passport")(passport);
app.get("/", (req, res) => {
res.json({ msg: "Nothing here mate" });
});
//------------ THIS IS WORKING ----------------
app.get("/db/:database/navigation", (req, res) => {
var dbname = req.params.database;
var conn = connections[dbname];
var navs = conn.model("navigation", app.models[dbname].navigation);
// Send json data/error
if (navs) navs.find({}, (err, data) => res.json(data));
else res.json({ error: true, msg: "Model not found" });
});
// -------------------------------------------------------
// Setup databases for all projects
_.each(dbconfig.databases, db => {
var appModels = require("./app/models/index");
var processed = 0;
// We will use prefix for all routes /db/:database/
var routePrefix = "/db/" + db.name;
// Use user section
app.use(routePrefix + "/user", user);
// Connection callback - we need to wait for modules to initialize
var connect = () => {
// Initialize connection
connections[db.name] = new mongoose.Mongoose().createConnection(db.url);
// Create some callbacks
connections[db.name].on("connected", () => { console.log("Connected to database " + db.url); });
connections[db.name].on("error", onDatabaseError)
// Once we initialize connection, we need to setup all routes
connections[db.name].once("open", function () {
// Load routes
var routes = require('./app/routes');
// Loop trough routes and use all of them
_.each(routes, function (controller, route) {
var newRoute = routePrefix + route;
app.use(newRoute, controller(app, newRoute, db.name));
});
});
};
// Initialize models
_.each(appModels, (model, index) => {
// Create object if doenst exist
if (app.models == null)
app.models = {};
if (app.models[db.name] == null) {
app.models[db.name] = { [model.name]: model.model };
}
else {
app.models[db.name] = Object.assign(app.models[db.name], { [model.name]: model.model });
}
processed++;
// if this was the last process we are ready to connect
if (processed === appModels.length)
connect();
});
});
app.listen(3000);
app/models/index.js
module.exports = [
{
name: "navigation",
model: require('./Navigation.js')
},
...
];
app/routes.js
module.exports = {
'/navigation': require('./controllers/NavigationController'),
....
};
app/controllers/NavigationController.js
var restful = require("node-restful");
module.exports = function(app, route, dbname){
console.log(route);
var rest = restful.model(
"navigation",
app.models[dbname].navigation
).methods(['get', 'put', 'post', 'delete']);
rest.register(app, route);
// Return middleware
return function(req, res, next){
next();
};
};
Navigation.js is basically just a schema. If I set up route manually like this:
app.get("/db/:database/navigation", (req, res) => {
var dbname = req.params.database;
var conn = connections[dbname];
var navs = conn.model("navigation", app.models[dbname].navigation);
// Send json data/error
if (navs) navs.find({}, (err, data) => res.json(data));
else res.json({ error: true, msg: "Model not found" });
});
it works just fine. I guess I need to assign connection somewhere to restful but I have no idea where. If I use single connection with mongoose.connect() everything works perfectly, but that's not what I need :)
Does anyone have any idea what to do next to get this to work? Will appreciate any help, thanks.
Kind of dug into it and made an extension to change driver reference
Here are all the changes, hope it helps someone in future :)
extensions/restful/index.js
var restful = require("node-restful"),
model = require("./model"),
mongoose = require('mongoose');
exports = module.exports = restful;
exports.model = model;
exports.mongoose = mongoose;
extensions/restful/model.js
exports = module.exports = model;
exports.changeDriver = function(driver){
mongoose = driver;
}
// original model function from node-restful/lib/model.js
function model() {
var result = mongoose.model.apply(mongoose, arguments),
default_properties = defaults();
if (1 === arguments.length) return result;
for (var key in default_properties) {
result[key] = default_properties[key];
}
return result;
}
app/controllers/navigation.js
var restful = require("../extensions/restful");
module.exports = function(app, route, db)
{
restful.model.changeDriver(db.mongoose);
// Setup controller REST
var rest = restful.model(
"navigation",
app.models[db.name].navigation
).methods(['get', 'put', 'post', 'delete']);
// Register this endpoint with the application
rest.register(app, route);
// Return middleware
return function(req, res, next){
next();
};
};
index.js
....
var connect = () => {
// Initialize connection
var $db = databases[_db.name] = {
mongoose: new mongoose.Mongoose(),
name: _db.name
};
$db.mongoose.connect(_db.url);
// Create some callbacks
$db.mongoose.connection.on("connected", () => { console.log("Connected to database " + _db.url); });
$db.mongoose.connection.on("error", (err) => { console.log("Database error: " + err); });
// Once we initialize connection, we need to setup all routes
$db.mongoose.connection.once("open", function () {
// Custom routes for user section
var userRoutes = new UserRoutes($db.mongoose);
app.use(routePrefix + "/user", userRoutes.getRouter());
// Load routes
var routes = require('./app/routes');
// Loop trough routes and use all of them
_.each(routes, function (controller, route) {
var newRoute = routePrefix + route;
app.use(newRoute, controller(app, newRoute, $db));
});
});
};
....

TypeError: Cannot read property 'then' of undefined node js

TypeError: Cannot read property 'then' of undefined
Can you help me fix this? Thank you.
var express = require('express');
var path = require('path');
var bodyParser = require('body-parser');
var mongodb = require('mongodb');
var dbConn = mongodb.MongoClient.connect('mongodb://localhost:27017',
function(err, db) {
if(err){
throw err;
}else{
console.log("connected");
}
})
var app = express();
app.use(bodyParser.urlencoded({ extended: false }));
app.use(express.static(path.resolve(__dirname, './')));
app.post('/post-feedback', function (req, res) {
dbConn.then(function(db) {
delete req.body._id; // for safety reasons
db.collection('feedbacks').insertOne(req.body);
});
res.send('Data received:\n' + JSON.stringify(req.body));
});
app.get('/view-feedbacks', function(req, res) {
dbConn.then(function(db) {
db.collection('feedbacks').find({}).toArray().then(function(feedbacks) {
res.status(200).json(feedbacks);
});
});
});
app.listen(process.env.PORT || 3000, process.env.IP || '0.0.0.0' );
TypeError: Cannot read property 'then' of undefined
Can you help me fix this? Thank you.
The following approach should get you started but should not use this for production (Reference: How do I manage MongoDB connections in a Node.js web application?). Read through for another production starters.
var express = require('express');
var path = require('path');
var bodyParser = require('body-parser');
var mongodb = require('mongodb');
var dbConn = function() {
return new Promise((resolve, reject) => {
mongodb.MongoClient.connect('mongodb://localhost:27017',
function(err, db) {
if(err){
return reject(err);
}else{
return resolve(db);
}
});
});
}
var app = express();
app.use(bodyParser.urlencoded({ extended: false }));
app.use(express.static(path.resolve(__dirname, './')));
app.post('/post-feedback', function (req, res) {
dbConn()
.then(function(db) {
delete req.body._id; // for safety reasons
db.collection('feedbacks').insertOne(req.body);
res.send('Data received:\n' + JSON.stringify(req.body));
})
.catch(err => {
console.log(err)
res.send('Error');
})
});
app.get('/view-feedbacks', function(req, res) {
dbConn()
.then(function(db) {
db.collection('feedbacks').find({}).toArray().then(function(feedbacks) {
res.status(200).json(feedbacks);
});
})
.catch(err => {
console.log(err);
res.status(500).json({});
});
});
app.listen(process.env.PORT || 3000, process.env.IP || '0.0.0.0' );
Production Starter:
Ideally you will have something like following say in a file db.js
let mongoClient = require('mongodb').MongoClient,
logger = require('winston');
function DATABASE() {
this.dbObj = null;
this.myCollection = null; // You will need to add more collections here
}
DATABASE.prototype.init = function (config, options) {
let self = this;
self.config = config; //can pass a config for different things like port, ip etc.
self.logger = logger;
return new Promise(function (resolve, reject) {
if (self.initialized) {
return resolve(self);
}
let connectionUri = "mongodb://localhost:27017"; //self.config.mongo.connectionUri;
mongoClient.connect(connectionUri, {native_parser: true}, function (err, db) {
if (err) {
reject(err);
}
else {
self.dbObj = db;
self.myCollection = db.collection('myCollection');
self.initialized = true;
self.logger.info("db init success");
return resolve(self);
}
});
});
};
var dbObj = null;
var getdbObj = function () {
if (!dbObj) {
dbObj = new DATABASE();
}
return dbObj;
}();
module.exports = getdbObj;
In your main app start file you will have something like:
let dbObj = require('./db.js');
dbObj.init()
.then(db => {
console.log('db initialized successfully');
//db.dbObj.collection('myCollection').find()
//or
//db.myCollection.find() because this has been already initialized in db.js
var app = express();
app.use(bodyParser.urlencoded({ extended: false }));
app.use(express.static(path.resolve(__dirname, './')));
app.post('/post-feedback', function (req, res) {
delete req.body._id; // for safety reasons
db.dbObj.collection('feedbacks').insertOne(req.body);
res.send('Data received:\n' + JSON.stringify(req.body));
});
app.get('/view-feedbacks', function(req, res) {
//db.collection('feedbacks')
});
app.listen(process.env.PORT || 3000, process.env.IP || '0.0.0.0' )
})
.catch(err => console.log(err));
Try this, dbConn is not promise
app.post('/post-feedback', function (req, res) {
mongoose.connection.db.collection('feedbacks', function (err, collection) {
collection.insertOne(req.body);
res.send('Data received:\n' + JSON.stringify(req.body));
});
// OR
const Model = mongoose.model('feedbacks');
let model = new Model();
model = Object.assign(model, req.body);
model.save().then((result) => {
res.send('Data received:\n' + JSON.stringify(req.body));
});
});
Its working .
If you are getting any TypeError (UnhandledPromiseRejectionWarning: TypeError: db.collection is not a function) form mongodb. Just change the version of mongodb to -
"mongodb": "^2.2.33"
"use strict"
var express = require('express');
var mongodb = require('mongodb');
var app = express();
var MongoClient = mongodb.MongoClient;
var url = 'mongodb://localhost:27017/feedback';
// no need to call then() yet
var dbConn = MongoClient.connect(url);
app.set('port', 5000);
app.listen(app.get('port'), function() {
console.log('feedback is running on port', app.get('port'));
});
app.get('/view-feedback', function(req, res, next) {
// the connection is opened
dbConn.then(function(db) {
// var dbo = db.db("feedback");
db.collection('feedback').find({}).toArray().then(function(docs) {
// return docs;
res.json(docs)
});
});
});

Node + Q with expressjs - ordered promisses

I want to execute a set of functions in the order they were written and in the end to release the request to the client.
for example see the mock code bellow:
router.get('/dashboard', function(req, res, next) {
var json = {items : 0}
Q.fcall(
function(){
//first exec
json.items+=1;
}
).then(
function(){
//scond exec
json.items+=1;
}
).then(
function(){
//third exec
json.items+=1;
}
).finally(
function(){
//do this when all the other promises are don
res.json(json);
});
}
the finally function shoud be executed when all is done.
Can it be done with Q?
UPDATE
I think I mislead you, and did not give all the information, because i did not think its relevant, but it is...
I actually bringing data via mongoose, and mongoose is async asd well.
So it goes like this:
Q.fcall(
function() {
Visitor.count(dateRange, function(err, data) {
json.newVisitors = data;
});
}).then(
function() {
Account.count(dateRange, function(err, data) {
json.newAccounts = data;
});
}).finally(
function() {
res.json(json);
})
Mongoose is already promisified. Calling exec() on a query gives you a promise. Here are two ways of doing it:
Classic promises chaining:
Visitor.count(dateRange).exec().then(function (data) {
json.newVisitors = data;
return Account.count(dateRange).exec(); // return promise for chaining
}).then(function (data) {
json.newAccounts = data;
}).then(function () {
res.json(json);
}).catch(function (err) {
// handle errors
});
Or Promise.all:
Promise.all([
Visitor.count(dateRange).exec(),
Account.count(dateRange).exec()
]).then(function(result){
// result is an ordered array of all the promises result
json.newVisitors = result[0];
json.newAccounts = result[1];
}).catch(function (err) {
// handle errors
});
Yes:
var path = require('path'),
express = require('express'),
app = express(),
router = express.Router(),
Q = require('q');
router.get('/dashboard', function(req, res) {
var json = {items:''};
Q.fcall(function() {
json.items += 'A';
})
.then(function() {
json.items += 'B';
})
.then(function() {
json.items += 'C';
})
.finally(function() {
res.json(json);
});
});
app.use('/', router);
var http = require('http');
var port = process.env.PORT || '3000';
app.set('port', port);
var server = http.createServer(app);
server.listen(port);
server.on('listening', function onListening() {
var addr = server.address();
var bind = typeof addr === 'string'
? 'pipe ' + addr
: 'port ' + addr.port;
console.log('Listening on ' + bind);
}
);
Then
curl localhost:3000/dashboard
Returns:
{"items":"ABC"}
P.S. You might also want to investigate async-q et. al.:
async.series([
->
### do some stuff ###
Q 'one'
->
### do some more stuff ... ###
Q 'two'
]).then (results) ->
### results is now equal to ['one', 'two'] ###
doStuff()
.done()
### an example using an object instead of an array ###
async.series({
one: -> Q.delay(200).thenResolve(1)
two: -> Q.delay(100).thenResolve(2)
}).then (results) ->
### results is now equal to: {one: 1, two: 2} ###
doStuff()
.done()
UPDATED (a bit forced, I would just use async):
var path = require('path'),
express = require('express'),
app = express(),
logger = require('morgan'),
router = express.Router(),
Q = require('q'),
async = require('async-q');
app.set('views', path.join(__dirname, 'views'));
app.set('view engine', 'ejs');
app.use(logger('dev'));
router.get('/dashboard', function(req, res) {
var json = {};
async.series({
newVisitors: function() {
return Q.Promise(function(resolve,reject) {
console.log(arguments);
Visitor.count(dateRange, function(err, data) {
if(err) return reject(err);
resolve(data);
});
});
},
newAccounts: function() {
return Q.Promise(function(resolve,reject) {
Account.count(dateRange, function(err, data) {
if(err) return reject(err);
resolve(data);
});
});
}
})
.then(function(json) {
res.json(json);
});
});
app.use('/', router);
var http = require('http');
var port = process.env.PORT || '3000';
app.set('port', port);
var server = http.createServer(app);
server.listen(port);
server.on('listening', function onListening() {
var addr = server.address();
var bind = typeof addr === 'string'
? 'pipe ' + addr
: 'port ' + addr.port;
console.log('Listening on ' + bind);
}
);
Now returns:
{"newVisitors": 1,"newAccounts":2}

Resources