3

EventEmittersとハンドラーに関してCoffeeScript/NodeJSで非常に奇妙な動作が見られます。問題を示す小さなサンプルをまとめました...

基本的に、イベント処理に間接参照がありますが、最初のイベントハンドラーをラムダでラップしない限り、イベントを機能させることができないようです。この機能を実現するために何かできることがあるかどうかを理解したいと思います。 。基本的test1()に、以下は、私の考え方では、と同じ動作をする必要がありtest3()ます。test2()イベント処理の第2レベルが機能することを示すためだけに含まれています!

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')

最後に、太い矢印=>を使用して、クラス宣言でそのようなバインドされたメソッドを作成し、適切と思われるようにそれらを渡すことができます。、&​​cgo2: -> ...になります。go2: => ...しかし、この文脈では、それは奇妙な振る舞いだと思います。意味がより明確になるので、クロージャーを渡すことに固執します。

于 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 に答える