11

I've been working with Socket.io over the past few months, developing a fairly complex chat application with chat rooms, kicking/banning/moderators/friends/etc.

Over the course of development, I've rewritten the app several times and I'm still fighting with my code.

I really like JavaScript, but I find it really hard to maintain the application as it grows. I've read through huge amounts of "tutorials" on how to write chat applications, but they all cover just the most basic aspects. Same goes for all example apps on GitHub, and even most chat applications I found on the web (most of them are just simple IM without any user management).

Some use cases just seem too ridiculous to me, for example kicking a user from a room.

  • moderator clicks on a kick button -> emit event to the server
  • the server pairs the username with a socket (or just broadcast to all users and filter on the client side) -> emit to him kicked event
  • the user emits a logout event to the server and is also displayed a message that he was kicked (logout is just my implementation of punishment)
  • the user is removed from the chat room's user list -> emit current list of users to all users in the room

This doesn't seem too complex, but when I add all the callbacks that happen on the client side to manage the UI (since I use AngularJS, I use events to communicate between controllers), and also ton of callbacks on the server side, since everything is non-blocking, I find this really hard to test.

There's another problem on the client side, where I have to listen for the socket events in multiple places, so I have to have a kind of singleton global socket object, and hook event listeners in multiple places to it.

Am I doing something wrong, or is this callback hell the result of working with websockets with no way around it?

Are there any ways to make developing applications like this easier? For example alternative technology to Socket.io? So far I only found NowJS, which has last commit 5 months ago, and meteor, which is really cool, but looking at the website it doesn't really seem to be stable.

Jakub Arnold
  • 85,596
  • 89
  • 230
  • 327

5 Answers5

7

Just use promises my friend. jQuery has them built in, but on the nodejs side you can use the q library. Don't write async code without them! They take a little getting used to, but once you're in the mindset writing async code is a breeze.

There are also libraries like async which give you utility functions around callback code. But don't let the popularity fool you. Promises are sweet.

For async code testing you don't need to go much farther than nodeunit which gives you a simple done() function you can call when your test is done, so it can run as long as you like.

Andy Ray
  • 30,372
  • 14
  • 101
  • 138
  • 1
    You don't need to use jquery. I have seen a few other deferred libraries for JS. The awesome thing about all of them is that everyone just returns an object with done, fail and then, and most have a when function as well. Which I hope, means they are compatible with each other. – Parris Sep 24 '12 at 18:56
3

Whoa, let's break that into actual problems, as I don't see many defined:

Kicking user from a room:

  • moderator clicks the Remove button (emit should contain roomID and userID)
  • server code removes the user from the Room object and emits message to each user in the room that userID has been kicked. If you had user's socket join(roomID) earlier, server code for that would look like this:

    sio.sockets.to(roomID).emit('kicked', { userID: uid });
    

That's all. In the client code, you should receive this "kicked" event and have code like this:

if (data.removedUserID == myUserID)
    alert('You have been kicked by moderator');
else
    removeUserFromList(userID);

You should not have the client emit a message that he is leaving, as malicious users could write a client that ignores banning.

I have to listen for the socket events in multiple places

Why? What does "multiple places" mean exactly?

Are there any ways to make developing applications like this easier?

  • Don't just dive into code. Think how people would communicate directly. Clients are people sending messages, server is the post office. Translate the logic in code later.
  • Make sure that server is the only one in control and clients only give commands and display the state without doing any logic there.

I have developed a 4000 loc multiplayer game where chat is only a small portion of it. I have about 60.000 users playing it each day. Everything is plain socket.io code with some Express/EJS to get the screens up, and I cannot imagine it getting any simpler. Especially not by using some "magic" library that would hide all the communication from me and surely introduce it's own set of bugs that are waiting to be discovered and fixed.

Milan Babuškov
  • 59,775
  • 49
  • 126
  • 179
0

Disclosure: I'm the developer of scoop.

I had more or less the same problem and it boils down to the usual callback pyramid which can be solved with many libraries (there are dozens of them, just take a look.

I was very happy with step before I found a major drawback: you can't nest them (a step call which calls more step stuff). This was very important for me and I didn't liked all the other async libs that much because they provided too much of functionality which I wouldn't use anyway, so I wrote scoop.

It's a simple lib which tries to help as all the other async libs, modeled after step with some personal flavor. Take a look at the examples, it may fit for your needs.

Luminger
  • 2,144
  • 15
  • 22
0

You could also take a look at derby.js. It is a framework very similar to meteor but built on all the node.js "goodies" like npm, socket.io, express, etc. Derby includes a powerful data synchronization engine called Racer that automatically syncs data among browsers, servers, and a database. They even have a basic chat example.

Meteor uses a lot of their own technology (fibers, own package manager). Like meteor derby is still in the alpha phase but gained a lot of attraction during the last time. Airbnb announced recently that they will consider derby for future implementations.

Some more information:

Community
  • 1
  • 1
zemirco
  • 16,171
  • 8
  • 62
  • 96
0

Your question is hard to answer, but I can assure you I feel your pain... Even without node.js, callbacks can get hairy pretty quickly, and async testing is really hard to do. I guess I should rather say: hard to do well, but that might sound like I know how to do it easily and I don't. The background problem is that async development is hard, just like concurrent programming used to be.

I don't think that a different websocket library is going to help you, or even avoiding websockets completely. What might help you is to use a few tricks. Andy Ray above suggests promises; I have not used them extensively but it is worth a try.

Self-diagnosis is your friend. JavaScript is a dynamic language without a type system worth its salt and that masks null objects; only a huge battery of automatic tests can ensure quality. But as you say testing can be really hard.

Another trick: instrument your application like crazy. Send instrumentation events and check for them in your tests. We have built a cool test suite around a headless browser (PhantomJS) where we check with JavaScript that the client is sending the right events; it can be hard to debug but it works.

Of course, the usual design tips help: KISS, YAGNI, and so on, but I will not insult your intelligence with them. Good luck.

alexfernandez
  • 1,938
  • 3
  • 19
  • 30