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
}
  1. トップ
  2. tech
  3. golang で event emitter/dispatcher 的なもの