{"id":16731670,"url":"https://github.com/spearwolf/eventize","last_synced_at":"2025-03-23T15:31:01.878Z","repository":{"id":4108229,"uuid":"51501784","full_name":"spearwolf/eventize","owner":"spearwolf","description":"yet another fantastic event emitter micro framework for javascript","archived":false,"fork":false,"pushed_at":"2024-09-27T07:42:14.000Z","size":5388,"stargazers_count":3,"open_issues_count":0,"forks_count":0,"subscribers_count":3,"default_branch":"main","last_synced_at":"2024-10-13T23:38:24.289Z","etag":null,"topics":["browser","eventemitter","nodejs"],"latest_commit_sha":null,"homepage":"","language":"TypeScript","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"apache-2.0","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/spearwolf.png","metadata":{"files":{"readme":"README.md","changelog":"CHANGELOG.md","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,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2016-02-11T08:11:53.000Z","updated_at":"2024-09-27T07:42:16.000Z","dependencies_parsed_at":"2023-07-05T17:15:47.955Z","dependency_job_id":"534c6871-bcdf-4679-8115-fb41319c5dc1","html_url":"https://github.com/spearwolf/eventize","commit_stats":{"total_commits":208,"total_committers":2,"mean_commits":104.0,"dds":0.06730769230769229,"last_synced_commit":"ac3bf81df6cce5945e06ac023133f0c782de8f7e"},"previous_names":[],"tags_count":10,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/spearwolf%2Feventize","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/spearwolf%2Feventize/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/spearwolf%2Feventize/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/spearwolf%2Feventize/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/spearwolf","download_url":"https://codeload.github.com/spearwolf/eventize/tar.gz/refs/heads/main","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":245122697,"owners_count":20564358,"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":["browser","eventemitter","nodejs"],"created_at":"2024-10-12T23:38:27.324Z","updated_at":"2025-03-23T15:31:01.492Z","avatar_url":"https://github.com/spearwolf.png","language":"TypeScript","funding_links":[],"categories":[],"sub_categories":[],"readme":"# @spearwolf/eventize\n\nA tiny and clever framework for synchronous event-driven programming in Javascript.\n\n![eventize hero image](hero.webp)\n\u003csmall\u003e\u003cem\u003eImage created in response to a request from spearwolf, using OpenAI's DALL-E, guided by ChatGPT.\u003c/em\u003e\u003c/small\u003e\n\n![npm (scoped)](https://img.shields.io/npm/v/%40spearwolf/eventize)\n![GitHub Workflow Status (with event)](https://img.shields.io/github/actions/workflow/status/spearwolf/eventize/main.yml)\n![GitHub](https://img.shields.io/github/license/spearwolf/eventize)\n\n\n## Introduction 👀\n\nYes, you read that right: the event emitters here call the subscribers _synchronously_ and not _asynchronously_ like in [node.js events](https://nodejs.org/api/events.html) for example.\n\nThis is perfectly reasonable: sometimes you want to have control over when something happens, e.g. when your code runs inside an [animation frame](https://developer.mozilla.org/en-US/docs/Web/API/window/requestAnimationFrame). Or you might want to free resources immediately and instantly.\n\n\n### FEATURES\n\n- :rocket: **smart api** with focus on developer experience\n- :sparkles: **wildcards** \u0026amp;❗**priorities**❕\n- includes **typescript types** (well, actually it is written in typescript) :tada:\n- supports all major browsers and node.js environments, targeting `ES2022`\n- very small footprint ~3k gzip'd\n- no runtime dependencies\n- Apache 2.0 licence\n\n\n## ⚙️ Installation\n\nAll you need to do is install the package:\n\n```sh\n$ npm i @spearwolf/eventize\n```\n\nThe package exports the library in _esm_ format (using `import` and `export` syntax) and also in _commonjs_ format (using `require`).\nIt is compiled with `ES2022` as target, so there are no downgrades to older javascript syntax and features.\n\nThe typescript type definitions are also included in the package.\n\n\u003e [!NOTE]\n\u003e  Since version 3.0.0 there is also a [CHANGELOG](./CHANGELOG.md)\n\n\n## 📖 Getting Started\n\nThe underlying concept is simple: certain types of objects (called \"emitters\") emit named events that cause function \"listeners\" to be called.\n\n![Emitter emits named event to listeners](./docs-assets/emitter-emits-named-events-listeners.svg)\n\n##### Emitter\n\n\u003e [!NOTE]\n\u003e _Emitter_ is a synonym for an _eventized object_, which in turn is a synonym for an object instance that has the _eventize superpowers_ attached to it!\n\u003e In this documentation we also use __ε__ as a variable name to indicate that it is an _eventized object_.\n\n\nAny object can become an _emitter_; to do so, the object must be _upgraded_:\n\n```js\nimport {eventize} from '@spearwolf/eventize'\n\n// !!! THIS IS THE RECOMMENDED AND MOST DIRECT APPROACH TO CREATE AN EVENTIZED OBJECT !!!\n\nconst eventizedObj = eventize(obj)\n```\n\n\u003e [!NOTE]\n\u003e If you don't want to specify an object, just leave it out and `{}` will be created for you: `const ε = eventize()` \n\nor, if you are more familiar with class-based objects, you can use\n\n```js\nimport {Eventize} from '@spearwolf/eventize'\n\nclass Foo extends Eventize {}\n\nconst ε = new Foo()\n\n// ε is now an object with eventize superpowers 🚀\n```\n\nFor __typescript__, the following _composition over inheritance_ variant has also worked well:\n\n```ts\nimport {eventize, type Eventize} from '@spearwolf/eventize'\n\nexport interface Foo extends Eventize {}\n\nexport class Foo {\n  constructor() {\n    eventize.inject(this);\n  }\n}\n```\n\nSince version 4.0.0 there is the _functional_ eventize API, so it is now possible to use `eventize()` in the constructor without any additions:\n\n```ts\nimport {eventize} from '@spearwolf/eventize'\n\nexport class Foo {\n  constructor() {\n    eventize(this);\n  }\n}\n```\n\n##### Listener or Subscriptions\n\nAny function can be used as a listener. However, you can also use an object that defines methods with the exact name of the given event.\n\n```js\n// ε is an eventized object\n\nε.on('foo', (bar) =\u003e {\n  console.log('I am a listener function and you called me with bar=', bar)\n})\n\nε.on('foo', {\n  foo(bar, plah) {\n    console.log('I am a method and you called me with bar=', bar, 'and plah=', plah)\n  }\n})\n\nε.on({\n  foo(bar, plah) {\n    console.log('foo -\u003e', {bar, plah})\n  },\n  bar() {\n    console.log('hej')\n  }\n})\n```\n\n##### Named Events\n\nAn emitter can emit any event name; parameters are optional\n\n```js\nε.emit('bar')\n// =\u003e \"hej\"\n\nε.emit('foo', 123, 456)\n// =\u003e \"I am a listener function and you called me with bar= 123\"\n// =\u003e \"I am a method and you called me with bar= 123 and plah= 456\"\n// =\u003e \"foo -\u003e {bar: 123, plah: 456}\"\n```\n\nIf an emitter emits an event to which no listeners are attached, nothing happens.\n\n\u003e 🔎 an event name can be either a _string_ or a _symbol_\n\n\n## 📚 API\n\n### How to _emitter_\n\n#### EventizedObject vs. EventizeApi\n\nTo give an object the eventize superpowers, it needs to be initialized once. for this purpose, there is the `eventize` function. The result is an `EventizedObject`.\nTo use the eventize API, the functions are available as named exports. The API currently includes the following functions:\n\n| function | description |\n|--------|-------------|\n| on | subscribe to events |\n| once | subscribe to the next event only |\n| onceAsync | the async version of subscribe only to the next event |\n| emit | dispatch an event |\n| emitAsync | dispatch an event and wait for any promises returned by subscribers |\n| off | unsubscribe |\n| retain | hold the last event until it is received by a subscriber |\n| retainClear | clear the last event |\n\n###### Example\n\n```typescript\nimport {eventize, on, emit} from '@spearwolf/eventize';\n\nconst obj = eventize();\n\non(obj, 'foo', () =\u003e console.log('foo called'));\n\nemit(obj, 'foo');  // =\u003e call foo subscriber\n```\n\n###### EventizeApi\n\nIf the `Eventize` base class or `eventize.inject()` is used instead of `eventize()`, an eventized object is also returned, but here additionally with the EventizeApi attached as as methods:\n\n```typescript\nimport {Eventize, on, emit} from '@spearwolf/eventize';\n\nclass Foo extends Eventize {}\nconst obj = new Foo();\n\nobj.on('foo', () =\u003e console.log('foo called'));\n\nobj.emit('foo');  // =\u003e call foo subscriber\n\nemit(obj, 'foo');  // =\u003e call foo subscriber\n```\n\n###### EventizedObject vs EventizeApi Overview Matrix\n\nThere are several ways to convert any object into an emitter / eventized object.\n\n| Method | is EventizedObject | has EventizeApi injected |\n|--------|--------------------|--------------------------|\n| `eventize(obj)` | ✅ | ❌ |\n| `eventize.inject(obj)` | ✅ | ✅ |\n| `class extends Eventize {}` | ✅ | ✅ |\n\n#### eventize\n\nThe easiest way to create an eventized object is to use the `eventize` function. The result is an object with eventize superpowers, which can be accessed using the eventize API functions:\n\n```ts\neventize( myObj )  // =\u003e myObj\n```\n\n#### eventize.inject\n\nAlternatively, it is possible to create an eventized object which has the complete eventize api injected as methods at the same time\n\n```ts\neventize.inject( myObj )  // =\u003e myObj\n```\n\nReturns the same object, with the eventize api attached, by modifying the original object.\n\n![eventize.inject](./docs-assets/eventize-inject.svg)\n\n\n#### Class-based inheritance\n\nThe class-based approach is essentially the same as the _extend_ method, but differs in how it is used:\n\n```js\nimport {Eventize} from '@spearwolf/eventize'\n\nclass Foo extends Eventize {\n  // constructor() {\n  //   super()\n  // }\n}\n```\n\n#### Class-based, without inheritance\n\nIf you want to create an emitter class-based, but not via inheritance, you can also use the eventize method in the constructor, here as a typescript example:\n\n```ts\nimport {eventize, Eventize} from '@spearwolf/eventize'\n\ninterface Foo extends Eventize {}\n\nclass Foo {\n  constructor() {\n    eventize.inject(this)\n  }\n}\n```\n\n---\n\n### Eventize API\n\nEach _emitter_ / _eventized_ object provides an API for subscribing, unsubscribing and emitting events.\nThis API is called the __eventize API__ (because \"emitter eventize API\" is a bit too long and cumbersome).\n\n| method | description |\n|--------|-------------|\n| `on( .. )` | subscribe to events |\n| `once( .. )` | subscribe only to the next event |\n| `onceAsync( .. )` | the async version of subscribe only to the next event |\n| `off( .. )` | unsubscribe |\n| `retain( .. )` | hold the last event until it is received by a subscriber |\n| `retainClear( .. )` | clear the last event |\n| `emit( .. )` | dispatch an event |\n| `emitAsync( .. )` | dispatch an event and waits for all promises returned by the subscribers |\n\nAll methods can be used in the _functional_ variant:\n\n```js\non(obj, ...)\nemit(obj, ...)\n// ..\n```\n\nthe objects that have been injected with the eventize api also offer the api as methods:\n\n```js\nobj.on(...)\nobj.emit(...)\n// ..\n```\n\nThese API methods are described in detail below:\n\n### How to listen\n\n---\n\n#### on\n\n\u003e `on(ε, .. )`\n\u003e `ε.on( .. )`\n\nThe simplest and most direct way is to use a function to subscribe to an event:\n\n```js\nimport {eventize} from '@spearwolf/eventize'\n\nconst ε = eventize()\n\n// short version\nε.on('foo', (a, b) =\u003e {\n  console.log('foo -\u003e', {a, b});\n});\n\n// extended version\nconst unsubscribe = ε.on('foo', (a, b) =\u003e {\n  console.log('foo -\u003e', {a, b});\n});\n```\n\nThe listener function is called when the named event is emitted.\nThe parameters of the listener function are optional and will be filled with the event parameters later (if there are any).\n\nThe return value of `on()` is always the _inverse of the call_ \u0026mdash; the unsubscription of the listener.\n\n##### Wildcards\n\nIf you want to respond to _all_ events, not just a specific named event, you can use the _catch-em-all_ wildcard event `*`:\n\n```js\nε.on('*', (...args) =\u003e console.log('an event occured, args=', ...args))\n```\n\nIf you wish, you can simply omit the wildcard event:\n\n```js\nε.on((...args) =\u003e console.log('an event occured, args=', ...args))\n```\n\n##### Multiple event names\n\nInstead of using a wildcard, you can specify multiple event names:\n\n```js\nε.on(['foo', 'bar'], (...args) =\u003e console.log('foo or bar occured, args=', ...args))\n```\n\n##### Priorities\n\nSometimes you also want to control the _order_ in which the listeners are called.\nBy default, the listeners are called in the order in which they are subscribed \u0026mdash; in their _priority group_; a priority group is defined by a number, where the default priority group is `0` and large numbers take precedence over small ones.\n\n```js\nε.on('foo', () =\u003e console.log(\"I don't care when I'm called\"))\nε.on('foo', -999, () =\u003e console.log(\"I want to be the last in line\"))\nε.on(Number.MAX_VALUE, () =\u003e console.log(\"I will be the first\"))\n\nε.emit('foo')\n// =\u003e \"I will be the first\"\n// =\u003e \"I don't care when I'm called\"\n// =\u003e \"I want to be the last in line\"\n```\n\n\n##### Listener objects\n\nYou can also use a listener object instead of a function:\n\n```js\nε.on('foo', {\n  foo(...args) {\n    console.log('foo called with args=', ...args)\n  }\n})\n```\n\nThis is quite useful in conjunction with wildcards:\n\n```js\nconst Init = Symbol('init')  // yes, symbols are used here as event names\nconst Render = Symbol('render')\nconst Dispose = Symbol('dispose')\n\nε.on({\n  [Init]() {\n    // initialize\n  }\n  [Render]() {\n    // show something\n  }\n  [Dispose]() {\n    // dispose resources\n  }\n})\n```\n\n.. or multiple event names:\n\n```js\nε.on(['init', 'dispose'], {\n  init() {\n    // initialize\n  }\n  goWild() {\n    // will probably not be called\n  }\n  dispose()) {\n    // dispose resources\n  }\n})\n```\n\nOf course, this also works with priorities:\n\n```js\nε.on(1000, {\n  foo() {\n    console.log('foo!')\n  }\n  bar() {\n    console.log('bar!')\n  }\n})\n```\n\nAs a last option, it is also possible to pass the listener method as a _name_ or _function_ to be called in addition to the listener object.\n\n###### Named listener object method\n\n```js\nε.on('hello', 'say', {\n  say(hello) {\n    console.log('hello', hello)\n  }\n})\n\nε.emit('hello', 'world')\n// =\u003e \"hello world\"\n```\n\n###### Listener function with explicit context\n\n```js\nε.on(\n  'hello',\n  function() {\n    console.log('hello', this.receiver)\n  }, {\n    receiver: 'world'\n  });\n\nε.emit('hello')\n// =\u003e \"hello world\"\n```\n\n##### Complete on() method signature overview\n\nFinally, here is an overview of all possible call signatures of the `.on( .. )` method:\n\n```\n.on( eventName*, [ priority, ] listenerFunc [, listenerObject] )\n.on( eventName*, [ priority, ] listenerFuncName, listenerObject )\n.on( eventName*, [ priority, ] listenerObject )\n```\n\nAdditional shortcuts for the wildcard `*` syntax:\n\n```\n.on( [ priority, ] listenerFunc [, listenerObject] )\n.on( [ priority, ] listenerObject )\n```\n\n###### Legend\n\n| argument | type |\n|----------|------|\n| `eventName*` | _eventName_ or _eventName[]_ |\n| `eventName` | _string_ or _symbol_ |\n| `listenerFunc` | _function_ |\n| `listenerFuncName` | _string_ or _symbol_ |\n| `listenerObject` | _object_ |\n\n---\n\n#### once\n\n\u003e `once(ε, .. )`\n\u003e `ε.once( .. )`\n\n`once()` does exactly the same as `on()`, with the difference that the listener is automatically unsubscribed after being called, so the listener method is called exactly _once_. No more and no less \u0026ndash; there is really nothing more to say about _once_.\n\n\u003e [!NOTE]\n\u003e if called with multiple event names, the first called event wins\n\n```js\nε.once('hi', () =\u003e console.log('hello'))\n\nε.emit('hi')\n// =\u003e \"hello\"\n\nε.emit('hi')\n// =\u003e (nothing happens here)\n```\n\n---\n\n#### onceAsync\n\n\u003e `onceAsync(ε, eventName | eventName[] )`\n\u003e `ε.onceAsync( eventName | eventName[] )`\n\n_since v3.3.*_\n\nThis creates a promise that will be fulfilled if one of the given events is emitted.\n\n```js\n// at this point please do nothing, just wait\nawait ε.onceAsync('loaded')\n\n// a little later, somewhere else in the program\nε.emit('loaded')\n```\n\n---\n\n#### off\n\n\u003e `off(ε, .. )`\n\u003e `ε.off( .. )`\n\n##### The art of unsubscribing\n\nAt the beginning we learned that each call to `on()` returns an _unsubscribe function_. You can think of this as `on()` creating a _link_ to the _event listener_.\nWhen this _unsubscribe function_ is called, the _link_ is removed.\n\nSo far, so good. Now let's say we write code that should respond to a dynamically generated event name with a particular method, e.g:\n\n```js\nconst queue = eventize()\n\nclass Greeter {\n  listenTo(name) {\n    queue.on(name, 'sayHello', this)\n  }\n\n  sayHello() {\n    // do what must be done\n  }\n}\n\nconst greeter = new Greeter()\ngreeter.listenTo('suzuka')\ngreeter.listenTo('yui')\ngreeter.listenTo('moa')\n```\n\nTo silence our greeter, we would have to call the _unsubscribe function_ returned by `on()` for every call to `listenTo()`. Quite inconvenient. This is where `off()` comes in. With `off()` we can specifically disable one or more previously established _links_. In this case this would be\n\n```js\nqueue.off(greeter)\n```\n\n... this will cancel _all_ subscriptions from `queue` to `greeter`!\n\n##### All kinds of `.off()` parameters in the summary\n\n`.off()` supports a number of variants, saving you from caching unsubscribe functions:\n\n| `.off()` parameter | description |\n|-|-|\n| `ε.off(function)` | unsubscribe by function |\n| `ε.off(function, object)` | unsubscribe by function and object context |\n| `ε.off(eventName)` | unsubscribe by event name |\n| `ε.off(object)` | unsubscribe by object |\n| `ε.off()` | unsubscribe all listeners attached to ε |\n\n\u003e 🔎 For those with unanswered questions, we recommend a look at the detailed test cases [./src/off.spec.ts](./src/off.spec.ts)\n\n###### getSubscriptionCount()\n\nA small helper function that returns the number of subscriptions to the object. Very useful for tests, for example.\n\n```js\nimport {getSubscriptionCount} from '@spearwolf/eventize';\n\ngetSubscriptionCount(ε) // =\u003e number of active subscriptions\n```\n\n### How to emit events\n\n---\n\n#### emit\n\n\u003e `emit(ε, .. )`\n\u003e `ε.emit( .. )`\n\nCreating an event is fairly simple and straightforward:\n\n```js\nε.emit('foo', 'bar', 666)\n```\n\nThat's it. No return value. All subscribed event listeners are immediately invoked.\n\nThe first argument is the name of the event. This can be a _string_ or a _symbol_.\nAll other parameters are optional and will be passed to the listener.\n\nIf you want to send multiple events at once - with the same parameters - you can simply pass an array of event names as the first parameter:\n\n```js\nε.emit(['foo', 'bar'], 'plah', 666)\n```\n\n\n---\n\n#### emitAsync\n\n\u003e `emitAsync(ε, .. )`\n\u003e `ε.emitAsync( .. )`\n\n_since v3.1.*_\n\n```js\nconst results = await ε.emitAsync('load');\n```\n\nEmits an event and waits for all promises returned by the subscribers.\n\nUnlike the normal `emit()`, here it is taken into account whether the subscribers return _something_.\nIf so, then all results are treated as promises and only when all have been resolved are the results\nreturned as an array.\n\nAnything that is not `null` or `undefined` is considered a return value.\n\nIf there are no return values, then simply `undefined` is returned.\n\nAll arguments that are allowed in `emit()` are supported.\n\n\n---\n\n#### retain\n\n\u003e `retain(ε, eventName | eventName[] )`\n\u003e `ε.retain( eventName | eventName[] )`\n\n##### Emit the last event to new subscribers\n\n```js\nε.retain('foo')\n```\n\nWith `retain` the last transmitted event is stored. Any new listener will get the last event, even if it was sent before they subscribed.\n\n\u003e NOTE: This behaviour is similar to the `new ReplaySubject(1)` of _rxjs_. But somehow the method name `retain` seemed more appropriate here.\n\n---\n\n#### retainClear\n\n\u003e `retainClear(ε, eventName | eventName[] )`\n\u003e `ε.retainClear( eventName | eventName[] )`\n\n##### Clear the last event\n\n_since v3.3.*_\n\n```js\nε.retainClear('foo')\n```\n\nWith `retainClear()` the _retain mode_ for the event is kept, but if there is already an event that is stored, it will now be cleared.\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fspearwolf%2Feventize","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fspearwolf%2Feventize","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fspearwolf%2Feventize/lists"}