For this post we use the following implementation of the pubsub pattern: pubsub with a twist
Ok, this one goes against some key benefits of the pubsub pattern. A subscriber is not supposed to return any value because it is breaking the lose coupling feature of the pattern. But lose-coupling is not the only benefit of pubsub. The other benefit is that it provides a framework to link functions at runtime. Let’s consider a class Cat, that publishes the event “signal_presence” and a class “mouse” that subscribes to it:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 | class Cat comesHome: -> pubsub.publish "signal_presence" @ class Mouse constructor: -> pubsub.subscribe "signal_presence", @get_the_f_out_of_here get_the_f_out_of_here: -> console.log "A mouse runs away..." jerry = new Mouse stuart = new Mouse tom = (new Cat).comesHome() output: ======= A mouse runs away... A mouse runs away... |
The 0 to n mouse present in the room will be notified and do what mice do, hide in your toaster…
We can also have the same class subscribing and publishing the event:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 | class Person setName: (name) -> @name = name @ comesHome: -> pubsub.publish "signal_presence", @name pubsub.subscribe "signal_presence", @greetings leavesHome: -> pubsub.unsubscribe "signal_presence", @greetings greetings: (name) => console.log "#{@name} says: @{name}!" Rob = (new Person).setName("Rob").comesHome() Marta = (new Person).setName("Marta").comeHome() output: ======= Rob Says: Marta! |
Note that I used the fat arrow here so that the events callback preserve the context of the object and I can call @name.
Ok, so that was pubsub as we know it. Now, what if pubsub could return a value, it would change the implementation of the publish method from pubsub with a twist to:
1 2 3 4 5 6 7 8 9 10 11 | publish = (topic) -> event = cache[topic] if event and event.length>0 callbackCount = event.length res = [] while callbackCount-- if event[callbackCount] res.push event[callbackCount].apply {}, Array.prototype.slice.call(arguments, 1) # some pubsub enhancement: see http://html5stars.com/blog/2012/10/20/pubsub-with-a-twist/ publish topic+"_done" res |
The variable “res” has been added. So now we could have:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 | class Person setName: (name) -> @name = name @ comesHome: -> res = pubsub.publish "signal_presence", @name if res @answer(res.name) for person in res pubsub.subscribe "signal_presence", @howareyou leavesHome: -> pubsub.unsubscribe "signal_presence", @howareyou howareyou: (name) => console.log "#{@name} says: @{name}, how are you?" answer: (name) => console.log "#{@name} says: Doing well. thank you #{@name}" Rob = (new Person).setName("Rob").comesHome() Marta = (new Person).setName("Marta").comeHome() Paul = (new Person).setName("Paul").comeHome() output: ======= Rob Says: Marta, how are you? Marta Says: Doing well, thank you Rob Rob Says: Paul, how are you? Paul Says: Doing well, thank you Rob Marta Says: Paul, how are you? Paul Says: Doing well, thank you Marta |
I have added that because I had a specific need where I wanted to know if an object had certain feature, but did not want to know how many instances of this object have been created and look through them. For this kind of need we can optimize the enhancement described here and let “res” be a variable, not an array. So that it would return only the return value of the last subscriber. Since I am using pubsub a lot in this program, saving the time used to instanciate an array and GCing every time seemed significant.

