Почему я не могу делегировать обработчики событий 2 глубоко? - PullRequest
3 голосов
/ 31 марта 2011

Я вижу очень странное поведение в CoffeeScript / NodeJS относительно EventEmitters и обработчиков. Я собрал небольшой образец, который показывает проблему ...

По сути, у меня есть некоторая косвенность в обработке событий, но я не могу заставить ее работать, пока я не заверну первый обработчик событий в лямбду, и я хочу понять, почему / если есть что-то, что я мог бы сделать для сделать эту работу. По сути, 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

Ответы [ 2 ]

5 голосов
/ 31 марта 2011

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: => ... и т. Д. В этом контексте, однако, я думаю, что это странное поведение. Я бы придерживался мимолетных замыканий, потому что это делает смысл более ясным.

1 голос
/ 31 марта 2011

Я просто добавлю еще один ответ здесь, хотя я принял приведенный выше, на случай, если люди не читают комментарии ...

Фактическое решение моей проблемы, чтобы получитьповедение, которое я ищу, это использовать => 'жирную стрелку' в моих определениях класса, а не обычную -> 'тонкую стрелку', как я это сделал, чтобы привязать функции к экземпляру класса.Итак:

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