golang で JS 的な addEventListener/dispatchEvent 的なことをしたいときどうするか?
emission
JS にあるようなのと全く同じ様に「イベント名」でイベントの種類を識別して任意の「イベントオブジェクト」をやりとりする。
On/Off/Once とか jQuery にあるような便利メソッドがついてる。
リフレクションで型変換は隠蔽されているが、もしリスナーとエミッターとで型が食い違っていると、実行時エラーになる。
package main
import (
"github.com/chuckpreslar/emission"
"log"
)
type ClickEvent struct {
button string
}
type KeyEvent struct {
key string
}
func main() {
emitter := emission.NewEmitter()
emitter.On("click", func (ev *ClickEvent) {
log.Printf("onclick %v", ev)
})
emitter.On("key", func (ev *KeyEvent) {
log.Printf("onkey %v", ev)
})
emitter.Emit("click", &ClickEvent{ button : "left" })
emitter.Emit("click", &ClickEvent{ button : "right" })
emitter.Emit("key", &KeyEvent{ key : "A" })
// panic
emitter.Emit("key", &ClickEvent{ button : "right" })
// or also panic
emitter.On("key", func (ev *ClickEvent) {
log.Printf("onkey2 %v", ev)
})
emitter.Emit("key", &KeyEvent{ button : "right" })
}
go-pubsub
Pub/Sub/Leave というメソッドが生えている。Pub は Emit/Dispatch, Sub は addListener/On, Leave は removeListener/Off に対応する。
「イベント名」というパラメータが存在せず、型の選択によって自動的にディスパッチされる。よって型がマッチしなければリスナーは実行されないので、原理的に型変換の実行時エラーは発生しない (もし間違えた場合単に実行時になって「呼ばれない」ことに気付く)
package main
import (
"github.com/mattn/go-pubsub"
"log"
"time"
)
type ClickEvent struct {
button string
}
type KeyEvent struct {
key string
}
func main() {
ps := pubsub.New()
ps.Sub(func(ev *ClickEvent) {
log.Printf("onclick %v", ev)
})
ps.Sub(func(ev *KeyEvent) {
log.Printf("onkey %v", ev)
})
ps.Pub(&ClickEvent{button: "left"})
ps.Pub(&ClickEvent{button: "right"})
ps.Pub(&KeyEvent{key: "A"})
time.Sleep(100 * time.Millisecond)
}
サンプルコード書いてて気付いたが、Sub に登録した関数は Sub を呼んだ順あるいは Pub を呼んだ順に関係なく実行されうる? (ref. 登録された関数を go で呼んでる)
下記コードのように time.Sleep の代わりに Pub/Sub によって終了を待とうとするとたまに失敗する。(完全に終了を待つ方法がわからなかった)
package main
import (
"github.com/mattn/go-pubsub"
"log"
)
type ClickEvent struct {
button string
}
type KeyEvent struct {
key string
}
func main() {
ps := pubsub.New()
ps.Sub(func(ev *ClickEvent) {
log.Printf("onclick %v", ev)
})
ps.Sub(func(ev *KeyEvent) {
log.Printf("onkey %v", ev)
})
ps.Pub(&ClickEvent{button: "left"})
ps.Pub(&ClickEvent{button: "right"})
ps.Pub(&KeyEvent{key: "A"})
// waiting for processing all messages
// -> FAIL
done := make(chan bool)
ps.Sub(func(b bool) {
ps.Close()
done <- true
})
ps.Pub(true)
<-done
}