~ 3 min read
Maintaining References to Sockets with Express and Socket.io
I hit a frustrating problem when trying to use expressjs alongside socket.io recently. The solution may seem somewhat trivial, but I struggled with it for a while, until finally asking for help on the socket.io irc channel. I’m not sure if a better solution exists for my case, but this is what I have for now.
In the main route of my express app, I needed to iterate across an array, performing a REST call using each element as a parameter and spit the results back for socket.io to send out to the browser. To get some idea of what that would look like, imagine the following.
app.get('/', function(req, res){
for(var i=0; i < someArray.length; i++){
var someVariable = doSomething(function(err, results){
socket.emit('msg', results);
});
}
});
My problem then became, how do I emit in this way, given my use case? All examples I could find on the socket.io homepages demonstrate emitting msgs on connection, like this:
io.sockets.on('connection', function (socket) {
// send custom events to browser socket
socket.emit('hi', { 'i can send': 'json!' });
});
But I wanted to emit messages as part of a loop, within an app route. How would I do that? I could wrap the loop with the connection logic:
app.get('/', function(req, res){
io.sockets.on('connection', function (socket) {
for(var i=0; i < someArray.length; i++){
var someVariable = doSomething(function(err, results){
socket.emit('msg', results);
});
}
});
});
But if I do this, then my connection listener would be added on every refresh of the route. This would mean I’d end up with duplicate binding of event listeners and a load more results messages would be emitted than I’d expect. This would result in crazy behavior for any user who might choose to refresh your app.
Actually, the solution is rather simple although not entirely obvious given the nature of using node. If you maintain a reference to the socket as a global variable and then later emit upon that same socket, like so:
var global_socket;
io.sockets.on('connection', function (socket) {
global_socket = socket;
});
app.get('/', function(req, res){
for(var i=0; i < someArray.length; i++){
var someVariable = doSomething(function(err, results){
global_socket.emit('msg', results);
});
}
});
This way, a connection will be established and maintained and we won’t get duplicate bindings when refreshing the route of the app. Simple when you know how. I had a rather interesting discussion following this on irc about whether the use of express and socket.io together is warranted, given that socket.io supports use of variable passing and ultimately could entirely replace an express app.