3

我在 CoffeeScript/NodeJS 中看到一些关于 EventEmitters 和处理程序的非常奇怪的行为。我已经整理了一个小样本来展示这个问题......

本质上,我的事件处理有一些间接性,但除非我将第一个事件处理程序包装在 lambda 中,否则我似乎无法让它工作,我想了解为什么/如果有什么我可以做的事情来完成这项工作. test1()以我的思维方式,基本上下面应该具有与test3(). test2()包括在内只是为了表明第二级事件处理有效!

events = require "events"

class ExampleEmitter extends events.EventEmitter
    constructor: () ->
    go1: () -> 
        console.log("fire 1")
        @emit("test1", "there")
    go2: () -> 
        console.log("fire 2")
        @emit("test2", "there")

class ExampleHandler
    constructor: () ->
    handle: (x) -> console.log("hey", x)

test1 = () ->        
    handler  = new ExampleHandler()
    emitter1 = new ExampleEmitter()
    emitter2 = new ExampleEmitter()
    emitter1.on "test1", emitter2.go2
    emitter2.on "test2", handler.handle #this doesn't fire :(
    emitter1.go1()

test2 = () ->        
    handler  = new ExampleHandler()
    emitter1 = new ExampleEmitter()
    emitter2 = new ExampleEmitter()
    emitter1.on "test1", emitter2.go2
    emitter2.on "test2", handler.handle
    emitter2.go2()

test3 = () ->        
    handler  = new ExampleHandler()
    emitter1 = new ExampleEmitter()
    emitter2 = new ExampleEmitter()
    emitter1.on "test1", () -> emitter2.go2() #why must I wrap this?
    emitter2.on "test2", handler.handle
    emitter1.go1()

console.log "\ntest1"
test1()
console.log "\ntest2"
test2()
console.log "\ntest3"
test3()

这是输出:

test1
fire 1
fire 2

test2
fire 2
hey there

test3
fire 1
fire 2
hey there
4

2 回答 2

5

emitter1.on "test1", () -> emitter2.go2() #why must I wrap this?

因为如果你只是通过emitter2.go2,go2 将在根对象的上下文中调用(window在浏览器中;我不太了解 node.js)而不是emitter2. 一个函数本身对它所属的对象一无所知。实际上,您应该将闭包传递给您对 的两个调用on

为了让事情看起来更好一点,如果你的闭包不带任何参数,你可以省略括号。最终,您应该拥有如下所示的内容:

handler  = new ExampleHandler()
emitter1 = new ExampleEmitter()
emitter2 = new ExampleEmitter()
emitter1.on "test1", -> emitter2.go2()
emitter2.on "test2", -> handler.handle()
emitter1.go1()

如果您仍然不喜欢它的外观,那么下一个最好的方法是使用一个函数,通过创建这样的闭包将函数“绑定”到对象。但是,它不会为您节省任何打字时间,而且我认为它看起来很难看且难以阅读:

bindMethod = (obj, funcName) ->
    -> obj[funcName].apply(obj, arguments)

...

emitter1.on "test1", bindMethod(emitter2, 'go2')
emitter2.on "test2", bindMethod(handler, 'handle')

最后,您可以使用粗箭头=>在类声明中创建此类绑定方法,以便您可以随意传递它们。go2: -> ...会变成go2: => ..., &c。不过,在这种情况下,我认为这是一种奇怪的行为。我会坚持传递闭包,因为它使含义更清楚。

于 2011-03-30T23:10:06.893 回答
1

我将在这里添加另一个答案,即使我已经接受了上面的答案,以防人们不阅读评论......

为了获得我正在寻找的行为,我的问题的实际解决方法是=>在我的类定义中使用“胖箭头”而不是->像我那样使用普通的“细箭头”,将函数绑定到实例班级。所以:

class ExampleEmitter extends events.EventEmitter
    constructor: () ->
    go1: () => 
        console.log("fire 1")
        @emit("test1", "there")
    go2: () => 
        console.log("fire 2")
        @emit("test2", "there")
于 2011-03-31T04:37:44.790 回答