Created
July 17, 2012 16:19
-
-
Save getify/3130393 to your computer and use it in GitHub Desktop.
extending the `EventEmitter` API with `either/or` pattern
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| EventEmitter.prototype.either = function(evt,cb) { | |
| var self = this, orAPI = {or: bindHandler}, handlers = {}; | |
| function bindHandler(evt,cb) { | |
| if (!handlers || handlers[evt]) return; | |
| handlers[evt] = function(){ | |
| for (var e in handlers) { | |
| self.removeListener(e,handlers[e]); | |
| } | |
| handlers = null; | |
| cb.apply(self,arguments); | |
| }; | |
| self.on(evt,handlers[evt]); | |
| return orAPI; | |
| } | |
| return bindHandler(evt,cb); | |
| }; |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| function Foo() { | |
| EventEmitter.call(this); | |
| } | |
| var foo = new Foo(); | |
| // ... | |
| foo | |
| .either("hello",function(){ alert("Hello"); }) | |
| .or("world",function(){ alert("World"); }) | |
| .or("yeah",function(){ alert("Yeah!"); }); | |
| /* Once any event in the set is fired, unbind all the listeners | |
| in this set. It's kind of like a `once()` that works across a | |
| set of listeners instead of just one. */ |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| // ok, so at the request of some in the community, here's a | |
| // more realistic usage for this pattern. | |
| // | |
| // Compare this version ("a") to the next one ("b"). This | |
| // version is probably the more traditional way such things | |
| // are done. Basically, you create a single event handler | |
| // that acts as a router by inspecting the data. | |
| var socket = io.connect(...); | |
| function login(u,p) { | |
| socket.once("login_status",function(data){ | |
| if (data.err) Error.handle(data); | |
| else UI.build(data); | |
| }); | |
| socket.emit("login",{u:u,p:p}); | |
| } | |
| UI = { | |
| build: function(data) { | |
| // build out the UI now that we're logged in | |
| } | |
| }; | |
| Error = { | |
| handle: function(data) { | |
| // display the error in a growl notice, for instance | |
| } | |
| } |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| // alternate way to do version "a" of the example, using | |
| // the `either/or` pattern. In this way, the event being | |
| // listened for effectively does the routing to different | |
| // handlers accordingly. | |
| var socket = io.connect(...); | |
| function login(u,p) { | |
| socket | |
| .either("login_successful", UI.build) | |
| .or("login_failed", Error.handle) | |
| socket.emit("login",{u:u,p:p}); | |
| } | |
| UI = { | |
| build: function(data) { | |
| // build out the UI now that we're logged in | |
| } | |
| }; | |
| Error = { | |
| handle: function(data) { | |
| // display the error in a growl notice, for instance | |
| } | |
| } |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
I think I follow some of your reasoning here. But if things get to a certain level of hairy with multiple event types, then you might be better served with a full blown state machine. I mean even in this scenario, you're not handling states that you didn't plan for. And generally when you get a message you didn't expect, that should be treated as an error.
It might feel onerous to do non-trivial dispatching if you do it ad-hoc and piece meal everywhere. But if you have a protocol layer, on both the client and the server, that interprets all messages before they are surfaced to the app, then things become much more manageable. The details of that stuff is hidden. In short, for anything non-trivial, I don't recommend dealing with socket.io sockets directly.
I'm sure I'm still missing some of the nuance of your use cases. And I'm not opposed to this idea at all. I just don't think it fits with my personal esthetic. For socket.io, I usually just have an
on('message')handler and interpret everything that comes over the wire.