{"id":13547805,"url":"https://github.com/nullobject/bulb","last_synced_at":"2025-04-12T08:13:25.949Z","repository":{"id":22860856,"uuid":"26208511","full_name":"nullobject/bulb","owner":"nullobject","description":"A reactive programming library for JavaScript.","archived":false,"fork":false,"pushed_at":"2024-06-12T03:15:55.000Z","size":2507,"stargazers_count":83,"open_issues_count":0,"forks_count":3,"subscribers_count":7,"default_branch":"main","last_synced_at":"2025-04-12T08:13:18.344Z","etag":null,"topics":["functional","functional-programming","javascript","reactive","reactive-programming"],"latest_commit_sha":null,"homepage":"https://bulb.joshbassett.info","language":"JavaScript","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/nullobject.png","metadata":{"files":{"readme":"README.md","changelog":"CHANGELOG.md","contributing":null,"funding":null,"license":null,"code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null}},"created_at":"2014-11-05T07:31:24.000Z","updated_at":"2024-06-12T03:15:59.000Z","dependencies_parsed_at":"2022-08-23T16:00:16.787Z","dependency_job_id":null,"html_url":"https://github.com/nullobject/bulb","commit_stats":null,"previous_names":[],"tags_count":25,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/nullobject%2Fbulb","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/nullobject%2Fbulb/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/nullobject%2Fbulb/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/nullobject%2Fbulb/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/nullobject","download_url":"https://codeload.github.com/nullobject/bulb/tar.gz/refs/heads/main","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":248537144,"owners_count":21120711,"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":["functional","functional-programming","javascript","reactive","reactive-programming"],"created_at":"2024-08-01T12:01:01.285Z","updated_at":"2025-04-12T08:13:25.944Z","avatar_url":"https://github.com/nullobject.png","language":"JavaScript","readme":"\u003ch1 align=\"center\"\u003e\u003cimg alt=\"Bulb\" src=\"https://raw.githubusercontent.com/nullobject/bulb/master/logo.png\" width=\"200px\" /\u003e\u003c/h1\u003e\n\n[![Build Status](https://travis-ci.com/nullobject/bulb.svg?branch=master)](https://travis-ci.com/nullobject/bulb)\n\nBulb is a [reactive\nprogramming](https://en.wikipedia.org/wiki/Reactive_programming) library for\nJavaScript. It provides a simple API for writing event-based programs in a\ndeclarative style.\n\nThe main data structure introduced by Bulb is called a *signal*. A signal\nrepresents a time-varying source of values \u0026mdash; for example, the value of a\ntext input, a periodic timer, or the position of the mouse pointer in the\nbrowser window.\n\nThe Bulb API provides many functions for creating signals from various sources\n(e.g. arrays, timers, AJAX requests, DOM events, etc.) and for modifying\nsignals using *combinators*.\n\nA number of libraries already exist for reactive programming in JavaScript\n(e.g. RxJS, Bacon.js, Most.js), but Bulb differs in that it tries to avoid\nproviding a \"kitchen sink\". Instead, Bulb defines a very focussed API that\ncontains only the key building blocks for reactive programming in JavaScript.\n\nFeatures:\n\n* Implements the [ECMAScript Observables\n  proposal](https://github.com/tc39/proposal-observable).\n* Simple, focused API. Bigger isn't always better.\n* It's small, roughly 4 KB when minified and gzipped.\n\n## Table of Contents\n\n* [Installation](#installation)\n  * [Node](#node)\n  * [Browser](#browser)\n* [Documentation](#documentation)\n  * [What is a Signal?](#what-is-a-signal-traffic_light)\n  * [Signals at a Glance](#signals-at-a-glance-eyes)\n  * [Combinators](#combinators-revolving_hearts)\n  * [The Signal Life Cycle](#the-signal-life-cycle-recycle)\n  * [Get on the Bus](#get-on-the-bus-bus)\n* [Examples](#examples)\n* [Licence](#licence)\n\n## Installation\n\n### Node\n\nInstall the npm package:\n\n```sh\n\u003e npm install bulb\n```\n\nRequire it in your code:\n\n```js\nimport { Signal } from 'bulb'\n```\n\n### Browser\n\nThe easiest way to start using Bulb in your browser is to include it with a\n`\u003cscript\u003e` tag in your HTML file:\n\n```html\n\u003cscript src=\"https://unpkg.com/bulb/dist/bulb.min.js\"\u003e\u003c/script\u003e\n```\n\n## Documentation\n\n* [API documentation](http://bulb.joshbassett.info)\n* Article by Josh Bassett: [Bulb: A Reactive Programming Library for JavaScript](https://joshbassett.info/2018/bulb/)\n\n### What is a Signal? :traffic_light:\n\nThe term *signal* is borrowed from hardware description languages, which allow\nelectrical signals to be modelled as they travel through circuits. Much like in\ncircuits, signals represent a time-varying flow of data – they can be split\napart, joined together, and modified as they travel through a system.\n\nSignals are:\n\n* *Directed*: Data travels through a network of signals in only one direction.\n* *Composable*: Signals can be composed together to create new signals.\n* *Lazy*: Signals don't do anything until they actually need to (i.e. an\n  *observer* has subscribed).\n\n### Signals at a Glance :eyes:\n\nLet's create a simple signal that emits some values and logs them to the\nconsole:\n\n```js\nimport { Signal } from 'bulb'\n\nconst s = Signal.of(1, 2, 3)\n\ns.subscribe({\n  next (a) { console.log(a) }\n})\n```\n\nHere, `Signal.of(1, 2, 3)` creates a new signal which emits some values in\norder. At this point, the signal won't actually do anything until an *observer*\nsubscribes to the signal.\n\nThe `subscribe` method subscribes an observer to the signal. This means that\nthe `next` callback will be called when the signal emits a value. In this case,\nit just prints the emitted values to the console.\n\nAn observer can also specify other callback types:\n\n* `next`: Called when the signal emits a value.\n* `error`: Called when the signal emits an error.\n* `complete`: Called when the signal has finished emitting events.\n\nThere is also a handy shortcut if you only want to know when the signal emits a\nvalue. In this case, you can just pass a single callback instead of an observer\nobject:\n\n```js\ns.subscribe(a =\u003e console.log(a))\n```\n\n### Combinators :revolving_hearts:\n\nLet's continue with our signal from the previous example, but use the `map`\ncombinator to modify the values before they are logged to the console:\n\n```js\nimport { Signal } from 'bulb'\n\nconst s = Signal.of(1, 2, 3)\nconst t = s.map(a =\u003e a + 1)\n\nt.subscribe(console.log) // 2, 3, 4\n```\n\nIn this example, we created a completely new signal `t`, by mapping a function\nover the original signal `s`. When we subscribe to the new signal `t`, the\nmodified values are printed in the console.\n\nAnother useful combinator is `scan`:\n\n```js\nimport { Signal } from 'bulb'\n\nconst s = Signal.of(1, 2, 3)\nconst t = s.scan((a, b) =\u003e a + b, 0)\n\nt.subscribe(console.log) // 0, 1, 3, 6\n```\n\nIn this example we created a signal `t`, that takes the values emitted by the\nsignal `s` and emits the running total of the values, starting from zero. The\nfunction `(a, b) =\u003e a + b` is called for every value emitted by the signal `s`,\nwhere `a` is the accumulated value, and `b` is the emitted value. Note that the\n`scan` combinator will emit the accumulated value for *every* value emitted by\nthe signal `s`.\n\nSome combinators wait until the signal has completed before they emit a value.\nThe `fold` combinator is similar to the `scan` combinator, but it differs in\nthat it doesn't emit intermediate values. The final value will only be emitted\nafter the signal has completed:\n\n```js\nimport { Signal } from 'bulb'\n\nconst s = Signal.of(1, 2, 3)\nconst t = s.fold((a, b) =\u003e a + b, 0)\n\nt.subscribe(console.log) // 6\n```\n\nIn this example, we created a signal `t`, that takes the values emitted by the\nsignal `s` and calculates the total of the emitted values, starting from zero.\nThe function `(a, b) =\u003e a + b` is called for every value emitted by the signal\n`s`, where `a` is the accumulated value, and `b` is the emitted value. Note\nthat the `scan` combinator will only emit the accumulated value once the signal\n`s` has completed.\n\n### The Signal Life Cycle :recycle:\n\nAs we saw previously, to subscribe an observer to a signal we use the\n`subscribe` method. This method returns a subscription handle, which we can use\nto unsubscribe from the signal at a later point in time.\n\nThis can be useful for dealing with infinite signals (infinite signals are\nsignals which never complete, they just keep emitting values forever):\n\n```js\nimport { Signal } from 'bulb'\n\nconst s = Signal.periodic(1000)\nconst subscription = s.subscribe(console.log) // 0, 1, 2, ...\n\n// Some time later...\nsubscription.unsubscribe()\n```\n\nIn this example, we called the `periodic` method to create a signal `s` that\nemits an increasing number every second. When we subscribe to the signal, we\nkeep a reference to the returned subscription handle. To stop receiving values\nfrom the signal, we call the `unsubscribe` method on the handle.\n\n### Get on the Bus :bus:\n\nA `Bus` is a special type of signal that can be connected with other signals:\n\n```js\nimport { Bus, Signal } from 'bulb'\n\nconst s = Signal.of(1, 2, 3)\nconst bus = new Bus()\nconst subscription = bus.subscribe(console.log)\n\nbus.connect(s)\n```\n\nIn this example, we created a bus and a signal. We connected the signal `s` to\nthe bus by calling the `connect` method, which means that any values emitted by\nthe signal `s` will be re-emitted by the bus.\n\nSometimes it is useful to manually emit values on a bus:\n\n```js\nimport { Bus } from 'bulb'\n\nconst bus = new Bus()\nconst subscription = bus.subscribe(console.log)\n\nbus.next(1)\nbus.next(2)\nbus.next(3)\n```\n\nIn this example, we created a bus and subscribed it to the console logger. We\nthen manually emitted some values by calling the `next` method on the bus.\n\n## Examples\n\nTake a look at some examples of how to use Bulb in the real world:\n\n* [React](https://codepen.io/nullobject/pen/LqdERw)\n* [Timer](https://codepen.io/nullobject/pen/wpjQoM)\n* [Mouse Position](https://codepen.io/nullobject/pen/eyGQdY)\n* [Keyboard State](https://codepen.io/nullobject/pen/qpYoMw)\n* [Book Search](https://codepen.io/nullobject/pen/QarojE)\n* [Random Strings](https://codepen.io/nullobject/pen/rpvaeg)\n* [PIN Pad](https://codepen.io/nullobject/pen/jYxzda)\n\n## Licence\n\nBulb is licensed under the MIT licence. See the\n[LICENCE](https://github.com/nullobject/bulb/blob/master/LICENCE.md) file for\nmore details.\n","funding_links":[],"categories":["JavaScript"],"sub_categories":[],"project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fnullobject%2Fbulb","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fnullobject%2Fbulb","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fnullobject%2Fbulb/lists"}