{"id":20197094,"url":"https://github.com/dldc-packages/pubsub","last_synced_at":"2025-12-11T21:14:39.805Z","repository":{"id":35207163,"uuid":"216353191","full_name":"dldc-packages/pubsub","owner":"dldc-packages","description":"📫 A simple pub/sub written in Typescript","archived":false,"fork":false,"pushed_at":"2024-05-04T16:24:29.000Z","size":2390,"stargazers_count":14,"open_issues_count":0,"forks_count":0,"subscribers_count":1,"default_branch":"main","last_synced_at":"2025-03-17T21:44:09.717Z","etag":null,"topics":["pubsub","subscription","typescript"],"latest_commit_sha":null,"homepage":"","language":"TypeScript","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/dldc-packages.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,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2019-10-20T11:47:26.000Z","updated_at":"2024-11-10T17:41:00.000Z","dependencies_parsed_at":"2023-02-18T14:45:43.926Z","dependency_job_id":"fea95798-a878-41d0-96e7-3a19e39e8924","html_url":"https://github.com/dldc-packages/pubsub","commit_stats":null,"previous_names":["dldc-packages/pubsub","etienne-dldc/suub"],"tags_count":46,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/dldc-packages%2Fpubsub","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/dldc-packages%2Fpubsub/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/dldc-packages%2Fpubsub/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/dldc-packages%2Fpubsub/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/dldc-packages","download_url":"https://codeload.github.com/dldc-packages/pubsub/tar.gz/refs/heads/main","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":248055275,"owners_count":21040156,"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":["pubsub","subscription","typescript"],"created_at":"2024-11-14T04:27:19.679Z","updated_at":"2025-12-11T21:14:39.791Z","avatar_url":"https://github.com/dldc-packages.png","language":"TypeScript","funding_links":[],"categories":[],"sub_categories":[],"readme":"# 📫 PubSub\n\n\u003e A pub/sub library written in TypeScript\n\n```\nnpm install @dldc/pubsub\n```\n\n## Gist\n\n```ts\nimport { createSubscription } from \"@dldc/pubsub\";\n\nconst mySub = createSubscription\u003cnumber\u003e();\n\nconst unsub = mySub.subscribe((num) =\u003e {\n  console.log(\"num: \" + num);\n});\n\nmySub.emit(45); // num: 45\n\nunsub(); // Unsubscribe the callback\n```\n\n## Guide\n\n### Creating a Subscription\n\nTo create a `Subscription` you need to import the `createSubscription` function\nand call it.\n\n```ts\nimport { createSubscription } from \"@dldc/pubsub\";\n\nconst subscription = createSubscription();\n```\n\nIf you use TypeScript, you need to pass a type parameter to the\n`createSubscription` function to define the type of the value associated with\nthe subscription.\n\n```ts\nimport { createVoidSubscription } from \"@dldc/pubsub\";\n\nconst numSubscription = createSubscription\u003cnumber\u003e();\n```\n\nIf you don't want your subscription to emit any value, you can use the\n`createVoidSubscription` function.\n\n```ts\nimport { createVoidSubscription } from \"@dldc/pubsub\";\n\nconst voidSubscription = createVoidSubscription();\n```\n\n### Subscribe and Unsubscribe\n\nYou have two ways to `subscribe` / `unsubscribe`.\n\n- Using the reference of the callback function\n\n```ts\nconst callback = () =\u003e {\n  /*...*/\n};\n\nsubscription.subscribe(callback);\n// later\nsubscription.unsubscribe(callback);\n```\n\n- Using a SubId (a string)\n\n```ts\nsubscription.subscribeById(\"mySubId\", () =\u003e {\n  /*...*/\n});\n// later\nsubscription.unsubscribeById(\"mySubId\");\n```\n\nIn both case the `subscribe[ById]` return a function that will unsubscribe:\n\n```ts\nconst unsub = subscription.subscribe(/*...*/);\n// later\nunsub();\n```\n\n### Emitting value\n\nTo emit a value and trigger all subscribed `callback` you need to call the\n`emit` method.\n\n```ts\nsubscription.emit(42);\n// for void subscription you don't need to pass any value\nvoidSubscription.emit();\n```\n\n### OnUnsubscribe\n\nThe `subscribe[ById]` methods accept a optional function after the callback,\nthis function will be called when this callback you are subscribing is\nunsubscribed.\n\n```ts\nsubscription.subscribe(\n  () =\u003e {\n    /* ... */\n  },\n  () =\u003e {\n    console.log(\"Unsubscribed !\");\n  },\n);\n\n// or with a subId\nsubscription.subscribeById(\n  \"mySub\",\n  () =\u003e {\n    /* ... */\n  },\n  () =\u003e {\n    console.log(\"Unsubscribed !\");\n  },\n);\n```\n\n### Unsubscribing all subscriptions\n\nYou can call `unsubscribeAll` method on a subscription to remove all callback.\nThis will also trigger the `onUnsubscribe` if any.\n\n```ts\nsubscription.unsubscribeAll();\n```\n\n### `Subscription` options\n\nThe `createSubscription` (or `createVoidSubscription`) functions accept an\noption object as parameter (all properties are optional):\n\n```ts\nconst sub = Subscription.create({\n  onFirstSubscription: () =\u003e {},\n  onLastUnsubscribe: () =\u003e {},\n  onDestroy: () =\u003e {},\n  maxSubscriptionCount: 10000,\n  maxRecursiveEmit: 1000,\n  maxUnsubscribeAllLoop: 1000,\n});\n```\n\n#### `onFirstSubscription`\n\n\u003e A function called when the number of subscribers goes from `0` to `1`\n\n#### `onLastUnsubscribe`\n\n\u003e A function called when the number of subscribers goes from `1` to `0`\n\n#### `onDestroy`\n\n\u003e A function called when the `destroy` method is called. Note that during this\n\u003e call the `Subscription` is already destroyed and you can't call `emit` or\n\u003e `subscribe` anymore.\n\n#### `maxSubscriptionCount`\n\n\u003e A number to limit the maximum number of simultaneous subscriptions (default is\n\u003e `10000`). This limit exist to detect infinit subscription loop.\n\n#### `maxRecursiveEmit`\n\n\u003e A number to limit the maximum recursive call of `emit` (default is `1000`).\n\u003e This limit exist to detect infinite loop where you `emit` in a `callback`.\n\n#### `maxUnsubscribeAllLoop`\n\n\u003e A number to limit the maximum recursive call of `subscribe` inside a\n\u003e `onUnsubscribe` callback (default is `1000`).\n\n### Testing if a callback / subId is subscribed\n\nThe `isSubscribed[ById]` methods let you test whether or not a callback / subId\nis currently subscribed\n\n```ts\nsubscription.isSubscribed(myCallback); // \u003c- boolean\nsubscription.isSubscribedById(\"my-sub-id\"); // \u003c- boolean\n```\n\n### Reading the number of active Subscriptions\n\nYou can call the `size` method to get the number of subscriptions.\n\n```ts\nsubscription.size();\n```\n\n### Destroying a Subscription\n\nYou can call the `destroy` method to destroy a subscription. This will\nunsubscribe all callback and call the `onDestroy` option if any.\n\n```ts\nsubscription.destroy();\n```\n\nOnce destroyed, calling `emit` or `subscribe[ById]` will throw an error. You can\nstill call the other methods but they will have no effect.\n\nYou can check if a subscription is destroyed by calling the `isDestroyed`\nmethod.\n\n```ts\nsubscription.isDestroyed(); // \u003c- boolean\n```\n\n## Some precisions\n\n#### Callback are called in the order they are subscribed.\n\n```ts\nimport { createSubscription } from \"@dldc/pubsub\";\n\nconst subscription = createSubscription\u003cnumber\u003e();\n\nsubscription.subscribe((value) =\u003e console.log(\"First callback: \" + value));\nsubscription.subscribe((value) =\u003e console.log(\"Second callback: \" + value));\n\nsubscription.emit(42);\n// Output:\n// First callback: 42\n// Second callback: 42\n```\n\n#### If you re-subscribe the same callback or id it will not re-do a subscription but instead move the subscription to the end.\n\nIn other words, calling `subscribe` on an already subscribed callback or subId\nwill not make the callback called twice. But it will move the callback at the\nend of the subscription list. In the case of a subId, the callback will be\nreplaced by the new one.\n\n```ts\nconst callback = (value: number) =\u003e console.log(\"Callback: \" + value);\nconst otherCallback = (value: number) =\u003e\n  console.log(\"Other callback: \" + value);\n\nsubscription.subscribe(callback);\nsubscription.subscribe(otherCallback);\nsubscription.subscribe(callback); // Moves the callback to the end\n\nsubscription.emit(42);\n// Output:\n// Other callback: 42\n// Callback: 42\n```\n\n#### If you call `unsubscribe` in a callback it will have effect immediatly.\n\nIf the callback you unsubscribe is supposed to run after the current callback,\nit will not be called.\n\n```ts\nconst cb1 = () =\u003e {\n  console.log(\"Callback 1\");\n  subscription.unsubscribe(cb2);\n};\n\nconst cb2 = () =\u003e {\n  console.log(\"Callback 2\");\n};\n\nsubscription.subscribe(cb1);\nsubscription.subscribe(cb2);\n\nsubscription.emit(42);\n// Output:\n// Callback 1\n```\n\n#### If you `subscribe` in a callback it will not be called immediatly.\n\nBut it will be in the next `emit`.\n\n```ts\nsubscription.subscribe((value) =\u003e {\n  console.log(\"First callback: \" + value);\n  subscription.subscribe((v) =\u003e console.log(\"New callback: \" + v));\n});\n\nsubscription.emit(42);\nsubscription.emit(43);\n// Output:\n// First callback: 42\n// First callback: 43\n// New callback: 43\n```\n\n#### If you `emit()` in a callback it will defer the call to after the current emit is done.\n\n```ts\nconst sb1 = (value: number) =\u003e {\n  console.log(\"First callback: \" + value);\n  if (value === 42) {\n    subscription.emit(43);\n  }\n};\nconst cb2 = (value: number) =\u003e {\n  console.log(\"Second callback: \" + value);\n};\n\nsubscription.subscribe(sb1);\nsubscription.subscribe(cb2);\n\nsubscription.emit(42);\n// Output:\n// First callback: 42\n// Second callback: 42\n// First callback: 43\n// Second callback: 43\n```\n\n#### If you `subscribe` / `unsubscribe` / `emit` in an `onUnsubscribed` it will behave in the same way as if it was in the callback itself\n\n#### Calling `destroy` will unsubscribe all callback and call the `onUnsubscribe` if any\n\nIn these `onUnsubscribe` callback the subscription is considered destroyed so\nyou can't call `emit` or `subscribe` anymore.\n\n```ts\nsubscription.subscribe(\n  () =\u003e console.log(\"Callback\"),\n  () =\u003e console.log(\"Unsubscribed\"),\n);\n\nsubscription.destroy();\n// Output:\n// Unsubscribed\n```\n\n#### Calling `destroy` on a destroyed subscription will have no effect\n\nThis is a no-op, it will not call `onDestroy` again.\n\n```ts\nsubscription.destroy();\nsubscription.destroy(); // No effect\n```\n\n#### The subscription is already considered destroyed when `onDestroy` is called\n\nThis means that you can't call `emit` or `subscribe` in the `onDestroy` callback\nand that `isDestroyed` will return `true` in the `onDestroy` callback.\n\n```ts\nconst subscription = createSubscription\u003cnumber\u003e({\n  onDestroy: () =\u003e {\n    console.log(\"Destroyed\");\n    console.log(\"Is destroyed: \" + subscription.isDestroyed());\n  },\n});\n\nsubscription.destroy();\n// Output:\n// Destroyed\n// Is destroyed: true\n```\n\n## Scheduler [ADVANCED]\n\nAt the core of the `Subscription` is a scheduler that will manage the different\ncallbacks and their order of execution. If you need a single subscription or\nevent multiple that don't interact with each other, you don't need to know about\nthe scheduler. But if you need for example to subscribe to a subscription in the\ncallback of another subscription then keep reading.\n\n### Resuse the same scheduler for multiple subscriptions\n\nYou can create a `Scheduler` unsing the `createScheduler` function. You can then\npass this scheduler as the first option of the `createSubscription` and\n`createVoidSubscription` functions.\n\n```ts\nimport { createScheduler, createSubscription } from \"@dldc/pubsub\";\n\nconst scheduler = createScheduler();\n\nconst sub1 = createSubscription(scheduler);\nconst sub2 = createSubscription(scheduler);\n```\n\nNote that the `createScheduler` function accept the same options as the\n`createSubscription` function. When you pass a scheduler to create a\nsubscription, you can also pass a second argument to specify a\n`onFirstSubscription` and `onLastUnsubscribe` function specific to this\nsubscription.\n\n```ts\nimport { createScheduler, createSubscription } from \"@dldc/pubsub\";\n\nconst scheduler = createScheduler();\n\nconst sub1 = createSubscription(scheduler, {\n  onFirstSubscription: () =\u003e {\n    console.log(\"First subscription\");\n  },\n  onLastUnsubscribe: () =\u003e {\n    console.log(\"Last unsubscribe\");\n  },\n});\n```\n\n### Destrying a scheduler\n\nNote that when you destroy a scheduler, all subscriptions that use this\nscheduler will be destroyed as well. Calling `.destroy()` on a subscription will\nactually call `.destroy()` on the scheduler.\n\n## API\n\n```ts\nexport type Unsubscribe = () =\u003e void;\nexport type OnUnsubscribed = () =\u003e void;\nexport type SubscriptionCallback\u003cT\u003e = (value: T) =\u003e void;\nexport type VoidSubscriptionCallback = () =\u003e void;\nexport type UnsubscribeAllMethod = () =\u003e void;\n\nexport interface SubscribeMethod\u003cT\u003e {\n  (\n    callback: SubscriptionCallback\u003cT\u003e,\n    onUnsubscribe?: OnUnsubscribed,\n  ): Unsubscribe;\n  (\n    subId: string,\n    callback: SubscriptionCallback\u003cT\u003e,\n    onUnsubscribe?: OnUnsubscribed,\n  ): Unsubscribe;\n}\n\nexport interface VoidSubscribeMethod {\n  (\n    callback: VoidSubscriptionCallback,\n    onUnsubscribe?: OnUnsubscribed,\n  ): Unsubscribe;\n  (\n    subId: string,\n    callback: VoidSubscriptionCallback,\n    onUnsubscribe?: OnUnsubscribed,\n  ): Unsubscribe;\n}\n\nexport interface IsSubscribedMethod\u003cT\u003e {\n  (subId: string): boolean;\n  (callback: SubscriptionCallback\u003cT\u003e): boolean;\n}\n\nexport interface UnsubscribeMethod\u003cT\u003e {\n  (subId: string): void;\n  (callback: SubscriptionCallback\u003cT\u003e): void;\n}\n\nexport interface VoidIsSubscribedMethod {\n  (subId: string): boolean;\n  (callback: VoidSubscriptionCallback): boolean;\n}\n\nexport interface VoidUnsubscribeMethod {\n  (subId: string): void;\n  (callback: VoidSubscriptionCallback): void;\n}\n\nexport interface ISubscription\u003cT\u003e {\n  subscribe: SubscribeMethod\u003cT\u003e;\n  unsubscribe: UnsubscribeMethod\u003cT\u003e;\n  unsubscribeAll: UnsubscribeAllMethod;\n  isSubscribed: IsSubscribedMethod\u003cT\u003e;\n  size: () =\u003e number;\n  emit: (newValue: T) =\u003e void;\n  destroy: () =\u003e void;\n  isDestroyed: () =\u003e boolean;\n}\n\nexport interface IVoidSubscription {\n  subscribe: VoidSubscribeMethod;\n  unsubscribe: VoidUnsubscribeMethod;\n  unsubscribeAll: UnsubscribeAllMethod;\n  isSubscribed: VoidIsSubscribedMethod;\n  size: () =\u003e number;\n  emit: () =\u003e void;\n  destroy: () =\u003e void;\n  isDestroyed: () =\u003e boolean;\n}\n\nexport interface ISubscriptionOptions {\n  onFirstSubscription?: () =\u003e void;\n  onLastUnsubscribe?: () =\u003e void;\n  onDestroy?: () =\u003e void;\n  maxSubscriptionCount?: number;\n  maxRecursiveEmit?: number;\n}\n```\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fdldc-packages%2Fpubsub","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fdldc-packages%2Fpubsub","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fdldc-packages%2Fpubsub/lists"}