{"id":13413392,"url":"https://github.com/olebedev/emitter","last_synced_at":"2025-05-16T14:05:01.989Z","repository":{"id":41300922,"uuid":"45939696","full_name":"olebedev/emitter","owner":"olebedev","description":"Emits events in Go way, with wildcard, predicates, cancellation possibilities and many other good wins","archived":false,"fork":false,"pushed_at":"2023-04-11T05:06:14.000Z","size":41,"stargazers_count":514,"open_issues_count":4,"forks_count":34,"subscribers_count":12,"default_branch":"master","last_synced_at":"2025-04-12T10:59:45.068Z","etag":null,"topics":["emitter","golang"],"latest_commit_sha":null,"homepage":"","language":"Go","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"other","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/olebedev.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null}},"created_at":"2015-11-10T20:56:36.000Z","updated_at":"2025-04-07T14:47:19.000Z","dependencies_parsed_at":"2024-01-08T15:34:39.673Z","dependency_job_id":null,"html_url":"https://github.com/olebedev/emitter","commit_stats":null,"previous_names":[],"tags_count":0,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/olebedev%2Femitter","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/olebedev%2Femitter/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/olebedev%2Femitter/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/olebedev%2Femitter/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/olebedev","download_url":"https://codeload.github.com/olebedev/emitter/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":254544146,"owners_count":22088807,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["emitter","golang"],"created_at":"2024-07-30T20:01:39.283Z","updated_at":"2025-05-16T14:05:01.971Z","avatar_url":"https://github.com/olebedev.png","language":"Go","readme":"# Emitter [![wercker status](https://app.wercker.com/status/e5a44746dc89b513ed28e8a18c5c05c2/s \"wercker status\")](https://app.wercker.com/project/bykey/e5a44746dc89b513ed28e8a18c5c05c2) [![Coverage Status](https://coveralls.io/repos/olebedev/emitter/badge.svg?branch=HEAD\u0026service=github)](https://coveralls.io/github/olebedev/emitter?branch=HEAD) [![godoc](http://img.shields.io/badge/godoc-reference-blue.svg?style=flat)](https://godoc.org/github.com/olebedev/emitter)\n\nThe emitter package implements a channel-based pubsub pattern. The design goals are to use Golang concurrency model instead of flat callbacks and to design a very simple API that is easy to consume.\n## Why?\nGo has expressive concurrency model but nobody uses it properly for pubsub as far as I can tell (in the year 2015). I implemented my own solution as I could not find any other that meets my expectations. Please, read [this article](#) for more information.\n\n\n## What it does?\n\n- [sync/async event emitting](#flags)\n- [predicates/middlewares](#middlewares)\n- [bi-directional wildcard](#wildcard)\n- [discard emitting if needed](#cancellation)\n- [merge events from different channels](#groups)\n- [shallow on demand type casting](#event)\n- [work with callbacks(traditional way)](#callbacks-only-usage)\n\n\n## Brief example\n\n```go\ne := \u0026emitter.Emitter{}\ngo func(){\n\t\u003c-e.Emit(\"change\", 42) // wait for the event sent successfully\n\t\u003c-e.Emit(\"change\", 37)\n\te.Off(\"*\") // unsubscribe any listeners\n}()\n\nfor event := range e.On(\"change\") {\n\t// do something with event.Args\n\tprintln(event.Int(0)) // cast the first argument to int\n}\n// listener channel was closed\n```\n\n## Constructor\n`emitter.New` takes a `uint` as the first argument to indicate what buffer size should be used for listeners. It is also possible to change the buffer capacity during runtime using the following code: `e.Cap = 10`.\n\nBy default, the emitter uses one goroutine per listener to send an event. You can change that behavior from asynchronous to synchronous by passing `emitter.Sync` flag as shown here: `e.Use(\"*\", emitter.Sync)`. I recommend specifying middlewares(see [below](#middlewares)) for the emitter at the begining.\n\n## Wildcard\nThe package allows publications and subscriptions with wildcard. This feature is based on `path.Match` function.\n\nExample:\n\n```go\ngo e.Emit(\"something:special\", 42)\nevent := \u003c-e.Once(\"*\") // search any events\nprintln(event.Int(0)) // will print 42\n\n// or emit an event with wildcard path\ngo e.Emit(\"*\", 37) // emmit for everyone\nevent := \u003c-e.Once(\"something:special\")\nprintln(event.Int(0)) // will print 37\n```\n\nNote that the wildcard uses `path.Match`, but the lib does not return errors related to parsing for this is not the main feature. Please check the topic specifically via `emitter.Test()` function.\n\n## Middlewares\nAn important part of pubsub package is the predicates. It should be allowed to skip some events. Middlewares address this problem.\nThe middleware is a function that takes a pointer to the `Event` as its first argument. A middleware is capable of doing the following items:\n\n1. It allows you to modify an event.\n2. It allows skipping the event emitting if needed.\n3. It also allows modification of the event's arguments.\n4. It allows you to specify the mode to describe how exactly an event should be emitted(see [below](#flags)).\n\nThere are two ways to add middleware into the event emitting flow:\n\n- via .On(\"event\", middlewares...)\n- via .Use(\"event\", middlewares...)\n\nThe first one add middlewares only for a particular listener, while the second one adds middlewares for all events with a given topic.\n\nFor example:\n```go\n// use synchronous mode for all events, it also depends\n// on the emitter capacity(buffered/unbuffered channels)\ne.Use(\"*\", emitter.Sync)\ngo e.Emit(\"something:special\", 42)\n\n// define predicate\nevent := \u003c-e.Once(\"*\", func(ev *emitter.Event){\n\tif ev.Int(0) == 42 {\n\t    // skip sending\n\t\tev.Flags = ev.Flags | emitter.FlagVoid\n\t}\n})\npanic(\"will never happen\")\n```\n\n\n## Flags\nFlags needs to describe how exactly the event should be emitted. The available options are listed [here](https://godoc.org/github.com/olebedev/emitter#Flag).\n\nEvery event(`emitter.Event`) has a field called`.Flags` that contains flags as a binary mask.\nFlags can be set only via middlewares(see above).\n\nThere are several predefined middlewares to set needed flags:\n\n- [`emitter.Once`](https://godoc.org/github.com/olebedev/emitter#Once)\n- [`emitter.Close`](https://godoc.org/github.com/olebedev/emitter#Close)\n- [`emitter.Void`](https://godoc.org/github.com/olebedev/emitter#Void)\n- [`emitter.Skip`](https://godoc.org/github.com/olebedev/emitter#Skip)\n- [`emitter.Sync`](https://godoc.org/github.com/olebedev/emitter#Sync)\n- [`emitter.Reset`](https://godoc.org/github.com/olebedev/emitter#Reset)\n\nYou can chain the above flags as shown below:\n```go\ne.Use(\"*\", emitter.Void) // skip sending for any events\ngo e.Emit(\"surprise\", 65536)\nevent := \u003c-e.On(\"*\", emitter.Reset, emitter.Sync, emitter.Once) // set custom flags for this listener\npintln(event.Int(0)) // prints 65536\n```\n\n## Cancellation\nGolang provides developers with a powerful control for its concurrency flow. We know the state of a channel and whether it would block a go routine or not. So, by using this language construct, we can discard any emitted event. It's a good practice to design your application with timeouts so that you cancel the operations if needed as shown below:\n\nAssume you have time out to emit the events:\n```go\ndone := e.Emit(\"broadcast\", \"the\", \"event\", \"with\", \"timeout\")\n\nselect {\ncase \u003c-done:\n\t// so the sending is done\ncase \u003c-time.After(timeout):\n\t// time is out, let's discard emitting\n\tclose(done)\n}\n```\n\nIt's pretty useful to control any goroutines inside an emitter instance.\n\n## Callbacks-only usage\nusing the emitter in more traditional way is possible, as well. If you don't need the async mode or you very attentive to the application resources, then the recipe is to use an emitter with zero capacity or to use `FlagVoid` to skip sending into the listener channel and use middleware as callback:\n\n```go\ne := \u0026emitter.Emitter{}\ne.Use(\"*\", emitter.Void)\n\ngo e.Emit(\"change\", \"field\", \"value\")\ne.On(\"change\", func(event *Event){\n\t// handle changes here\n\tfield := event.String(0)\n\tvalue := event.String(1)\n\t// ...and so on\n})\n```\n\n## Groups\nGroup merges different listeners into one channel.\nExample:\n```go\ne1 := \u0026emitter.Emitter{}\ne2 := \u0026emitter.Emitter{}\ne3 := \u0026emitter.Emitter{}\n\ng := \u0026emitter.Group{Cap: 1}\ng.Add(e1.On(\"first\"), e2.On(\"second\"), e3.On(\"third\"))\n\nfor event := g.On() {\n\t// handle the event\n\t// event has field OriginalTopic and Topic\n}\n```\nAlso you can combine several groups into one.\n\nSee the api [here](https://godoc.org/github.com/olebedev/emitter#Group).\n\n\n## Event\nEvent is a struct that contains event [information](https://godoc.org/github.com/olebedev/emitter#Event). Also, th event has some helpers to cast various arguments into `bool`, `string`, `float64`, `int` by given argument index with an optional default value.\n\nExample:\n```go\n\ngo e.Emit(\"*\", \"some string\", 42, 37.0, true)\nevent := \u003c-e.Once(\"*\")\n\nfirst := event.String(0)\nsecond := event.Int(1)\nthird := event.Float(2)\nfourth := event.Bool(3)\n\n// use default value if not exists\ndontExists := event.Int(10, 64)\n// or use dafault value if type don't match\ndef := event.Int(0, 128)\n\n// .. and so on\n```\n\n## License\nMIT\n","funding_links":[],"categories":["Messaging","消息","Go","Relational Databases","消息系统","\u003cspan id=\"消息-messaging\"\u003e消息 Messaging\u003c/span\u003e","消息传递","机器学习"],"sub_categories":["Search and Analytic Databases","Advanced Console UIs","高級控制台界面","SQL 查询语句构建库","检索及分析资料库","高级控制台界面","\u003cspan id=\"高级控制台用户界面-advanced-console-uis\"\u003e高级控制台用户界面 Advanced Console UIs\u003c/span\u003e","交流"],"project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Folebedev%2Femitter","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Folebedev%2Femitter","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Folebedev%2Femitter/lists"}