Promisify Socket.IO / EventEmitter

2019-04-10 00:09发布

问题:

I am a bit surprised by the fact that I don't find much when googling for Promisify Socket.IO. Is it that uncommon?

I also had troubles to promisify it myself:

Promise.promisifyAll(io)
io.onceAsync('connect')
.then((socket) => ...)
.catch((err) => console.log(error))

This always triggers the error case, I assume because .once is a callback with only one argument, where Promises expect the first one to be the error. Any idea how to deal with these kind of things?

回答1:

I can think of several reasons why promises aren't a good fit with socket.io and EventEmitter interfaces in general:

  1. For the most part, socket.io is an event driven interface and promises do not align architecturally with events that can occur more than once (since a promise is a one-shot device). Yes, you can use a promise for .connect(), but not for incoming messages. So, most people (myself included) probably don't think it makes sense to have half an interface using promises and the other half using event handlers. Likely better to use one model for the whole API.

  2. Promise.promisifyAll() requires node.js style async callbacks (with error value as the first argument and data as the second argument) and that isn't what any of the socket.io event handler callbacks use. To make promises work with something like the connect event you'd have to write your own custom promsification which is likely more work than just using the event handler it is written for.

An exception to the above might be if you are trying to coordinate the next occurrence of an event with other async events (something that isn't usually done) in which case promises might be useful for the coordination. As an example, supposed you wanted to know when three separate asynchronous operations were all complete, one of which was the next occurrence of a socket.io event. Then, it might make sense to manually promisify that event so you could use promises to coordinate your multiple async operations.

But for normal socket.io usage, promises just aren't a good architectural fit. Similarly, you wouldn't typically use promises for a click handler in a web page.


FYI, if you want to promisify just the connect operation, you could do that manually like this:

io.connectAsync = function(url, options) {
    return new Promise(function(resolve, reject) {
        io.connect(url, options);
        io.once('connect', function(socket) {
            resolve(socket);
        });
        io.once('connect_error', function() {
            reject(new Error('connect_error'));
        });
        io.once('connect_timeout', function() {
            reject(new Error('connect_timeout'));
        });
    });
}


io.connectAsync().then(function(socket) {
    // connected here
    socket.on('someMsg', function() {
       // process message here
    });
}, function(err) {
    // error connecting here
});