trying to understand a code fragment of an article on socket.io and redis

I'm trying to understand an article here, and now everything is clear but one code fragment, mentioned on pre-last code block, with a total of 1 to 17 lines, and this fragment is from line 1 to 9:

app.use(function(req,res,next) {
   redis.get(req.user.email, function(err, id) {
     if (err) next(err);
      req.emitToUser = function() {
        var soc = id && io.to(id);
        soc.emit.apply(soc, arguments);
     }
  });
});

and I think its some shortcomings in my javascript knowledge are the root cause. My knowledge over this code fragment:

problems:

Please make me clear.

Upvotes: 0

Views: 68

Answers (1)

jfriend00
jfriend00

Reputation: 707466

There are some issues with this code, but the general idea is to define a method on the req object req.emitToUser() for every incoming request that will allow some other route handler later in the chain to use that method to emit to the user who make the request. This is a common desire to want to connect a currently connected socket.io connection to the user making the http request.

Let's look at each line here:

redis.get(req.user.email, function(err, id) {

Look up the req.user.email in the redis database to get a socket.io id associated with that email that has previously been saved in that redis database.

if (err) next(err);

If it wasn't found in redis, make this request fail with an error.

req.emitToUser = function() {

Assign a new method to the current req object so that other route handlers later in the chain can use that method.

 var soc = id && io.to(id);

Look up the id value in socket.io to get the socket for that id. Technically io.to() doesn't return the socket, but it returns an object that you can call emit() on that will send to that socket.

soc.emit.apply(soc, arguments);

The role of soc.emit.apply(soc, arguments); is this:

  1. Execute the soc.emit() method
  2. Set the this value when executing that method to the soc object.
  3. Set the arguments when executing that method to whatever the arguments were that were passed to req.emitToUser(x, y, z) when it was called.

Here's a more concrete example:

function fn(a, b, c) {
    console.log(a, b, c);
}

fn.apply(null, [1, 2, 3]);

Using fn.apply(null, [1, 2, 3]); will be the same as:

fn(1, 2, 3);

Now, you'd likely never use .apply() in this exact way when the arguments are already known. The case for using it is when you have some arbitrary array that is passed to you (you don't know what's in it) and you want to pass those arguments along to some other function in the exact same order as they were given to you. That's what soc.emit.apply(soc, arguments); is doing. It's taking the arguments object (which is an array-like structure that represents the arguments that were passed to the parent function req.emitToUser() and passing those exact arguments on it sock.emit(). If you knew exactly how many arguments there would be, then you could hard-code that same code as this:

app.use(function(req,res,next) {
   redis.get(req.user.email, function(err, id) {
     if (err) next(err);
      req.emitToUser = function(msg, data) {
        var soc = id && io.to(id);
        soc.emit(msg, data);
     }
  });
});

But, .apply() creates a more generic solution that will work regardless of how many arguments were passed to req.emitToUser() as it will just pass all the arguments on to soc.emit().


This line of code is a bit suspect:

var soc = id && io.to(id);

It appears to be trying to protect against there not being a proper id returned from redis earlier. But, if there's no id, then soc will not be a valid object and the following like of code:

soc.emit.apply(soc, arguments);

will throw. So, the id && io.to(id) isn't really providing the proper protection. It appears this should more likely be:

app.use(function(req,res,next) {
   redis.get(req.user.email, function(err, id) {
     if (err) next(err);
      req.emitToUser = function() {
        if (id) {
            var soc = io.to(id);
            soc.emit.apply(soc, arguments);
        } else {
            // not sure what you want here, perhaps return an error
            // or throw a more meaningful exception
        }
     }
  });
});

Upvotes: 1

Related Questions