MCPcopy Index your code
hub / github.com/baconjs/bacon.js

github.com/baconjs/bacon.js @3.0.23

repository ↗ · DeepWiki ↗ · release 3.0.23 ↗ · + Follow
773 symbols 2,424 edges 307 files 116 documented · 15% updated 14mo ago★ 6,45958 open issues
README

Bacon.js

A functional reactive programming lib for TypeScript JavaScript, written in TypeScript.

Turns your event spaghetti into clean and declarative feng shui bacon, by switching from imperative to functional. It's like replacing nested for-loops with functional programming concepts like map and filter. Stop working on individual events and work with event streams instead. Combine your data with merge and combine. Then switch to the heavier weapons and wield flatMap and combineTemplate like a boss.

Here's the stuff.

Build Status BrowserStack Status NPM version Dependency Status devDependency Status

Install and Usage

Typescript

Bacon.js starting from version 3.0 is a Typescript library so you won't need any external types. Just Install using npm.

npm install baconjs

Then you can

import { EventStream, once } from "baconjs"

let s: EventStream<string> = once("hello")
s.log()

As you can see, the global methods, such as once are imported separately.

Check out the new API Documentation, that's now generated using Typedoc from the Typescript source code.

Modern ES6 Browser, Node.js v.12+

You can directly import Bacon.js as single aggregated ES6 module.

import * as Bacon from 'node_modules/baconjs/dist/Bacon.mjs';
Bacon.once("hello").log();

NPM, CommonJS, Node.js

If you're on to CommonJS (node.js, webpack or similar) you can install Bacon using npm.

npm install baconjs

Try it like this:

node
Bacon=require("baconjs")
Bacon.once("hello").log()

The global methods, such as once are available in the Bacon object.

Bower

For bower users:

bower install bacon

CDN / Script Tags

Both minified and unminified versions available on cdnjs.

So you can also include Bacon.js using

<script src="https://cdnjs.cloudflare.com/ajax/libs/bacon.js/2.0.9/Bacon.js"></script>
<script>
Bacon.once("hello").log()
</script>

AMD / require.js

Bacon.js is an UMD module so it should work with AMD/require.js too. Not tested lately though.

Github

Prefer to drink from the firehose? Download from Github master.

Intro

The idea of Functional Reactive Programming is quite well described by Conal Elliot at Stack Overflow.

Bacon.js is a library for functional reactive programming. Or let's say it's a library for working with events in EventStreams and dynamic values (which are called Properties in Bacon.js).

You can wrap an event source, say "mouse clicks on a DOM element" into an EventStream by saying

let $ = (selector) => document.querySelector(selector) 
var clickE = Bacon.fromEvent($("h1"), "click")

The $ helper function above could be replaced with, for instance, jQuery or Zepto.

Each EventStream represents a stream of events. It is an Observable, meaning that you can listen to events in the stream using, for instance, the onValue method with a callback. Like this:

clickE.onValue(() => alert("you clicked the h1 element") )

But you can do neater stuff too. The Bacon of Bacon.js is that you can transform, filter and combine these streams in a multitude of ways (see EventStream API). The methods map, filter, for example, are similar to same functions in functional list programming (like Underscore). So, if you say

let plusE = Bacon.fromEvent($("#plus"), "click").map(1)
let minusE = Bacon.fromEvent($("#minus"), "click").map(-1)
let bothE = plusE.merge(minusE)

.. you'll have a stream that will output the number 1 when the "plus" button is clicked and another stream outputting -1 when the "minus" button is clicked. The bothE stream will be a merged stream containing events from both the plus and minus streams. This allows you to subscribe to both streams with one handler:

bothE.onValue(val => { /* val will be 1 or -1 */ console.log(val) })

Note that you can also use the log method to log stream values to console:

bothE.log()

In addition to EventStreams, bacon.js has a thing called Property, that is almost like an EventStream, but has a "current value". So things that change and have a current state are Properties, while things that consist of discrete events are EventStreams. You could think mouse clicks as an EventStream and mouse cursor position as a Property. You can create Properties from an EventStream with scan or toProperty methods. So, let's say

let add = (x, y) => x + y
let counterP = bothE.scan(0, add)
counterP.onValue(sum => $("#sum").textContent = sum )

The counterP property will contain the sum of the values in the bothE stream, so it's practically a counter that can be increased and decreased using the plus and minus buttons. The scan method was used here to calculate the "current sum" of events in the bothE stream, by giving a "seed value" 0 and an "accumulator function" add. The scan method creates a property that starts with the given seed value and on each event in the source stream applies the accumulator function to the current property value and the new value from the stream.

Hiding and showing the result div depending on the content of the property value is equally straightforward

let hiddenIfZero = value => value == 0 ? "hidden" : "visible"
counterP.map(hiddenIfZero)
  .onValue(visibility => { $("#sum").style.visibility = visibility })

For an actual (though a bit outdated) tutorial, please check out my blog posts

API

Creating EventStreams and Properties

There's a multitude of methods for creating an EventStream from different sources, including the DOM, node callbacks and promises for example. See EventStream documentation.

Properties are usually created based on EventStreams. Some common ways are introduced in Property documentation.

Combining multiple streams and properties

You can combine the latest value from multple sources using combine, combineAsArray, combineWith or combineTemplate.

You can merge multiple streams into one using merge or mergeAll.

You can concat streams using concat or concatAll.

If you want to get the value of an observable but emit only when another stream emits an event, you might want to use sampledBy or its cousin withLatestFrom.

Latest value of Property or EventStream

One of the common first questions people ask is "how do I get the latest value of a stream or a property". There is no getLatestValue method available and will not be either. You get the value by subscribing to the stream/property and handling the values in your callback. If you need the value of more than one source, use one of the combine methods.

Bus

Bus is an EventStream that allows you to push values into the stream. It also allows plugging other streams into the Bus.

Event

There are essentially three kinds of Events that are emitted by EventStreams and Properties:

  • Value events that convey a value. If you subscribe using onValue, you'll only deal with values. Also map, filter and most of the other operators also deal with values only.
  • Error events indicate that an error has occurred. More on errors below!
  • End event is emitted at most once, and is always the last event emitted by an Observable.

If you want to subscribe to all events from an Observable, you can use the subscribe method.

Errors

Error events are always passed through all stream operators. So, even if you filter all values out, the error events will pass through. If you use flatMap, the result stream will contain Error events from the source as well as all the spawned stream.

You can take action on errors by using onError.

See also mapError, errors, skipErrors, Bacon.retry and flatMapError.

In case you want to convert (some) value events into Error events, you may use flatMap like this:

stream = Bacon.fromArray([1,2,3,4]).flatMap(function(x) {
  if (x > 2)
    return new Bacon.Error("too big")
  else
    return x
})

Conversely, if you want to convert some Error events into value events, you may use flatMapError:

myStream.flatMapError(function(error) {
  return isNonCriticalError(error) ? handleNonCriticalError(error) : new Bacon.Error(error)
})

Note also that Bacon.js operators do not catch errors that are thrown. Especially map doesn't do so. If you want to map things and wrap caught errors into Error events, you can do the following:

```js wrapped = source.flatMap(Bacon.try(

Extension points exported contracts — how you extend this code

Option (Interface)
@hidden [1 implementers]
types/optional.d.ts
Option (Interface)
@hidden [1 implementers]
src/optional.ts
StreamMap (Interface)
@hidden
src/groupby.ts
RetryContext (Interface)
(no doc)
types/retry.d.ts
FlatMapParams (Interface)
(no doc)
types/flatmap_.d.ts
Scheduler (Interface)
(no doc)
types/scheduler.d.ts
StateF (Interface)
(no doc)
types/withstatemachine.d.ts
Subscription (Interface)
(no doc)
types/bus.d.ts

Core symbols most depended-on inside this repo

series
called by 290
test/util/SpecHelper.ts
expectStreamEvents
called by 282
test/util/SpecHelper.ts
push
called by 179
src/bus.ts
expectPropertyEvents
called by 173
test/util/SpecHelper.ts
toProperty
called by 172
src/observable.ts
error
called by 162
test/util/SpecHelper.ts
toString
called by 131
src/optional.ts
map
called by 102
src/optional.ts

Shape

Function 506
Method 159
Class 78
Interface 30

Languages

TypeScript100%

Modules by API surface

src/observable.ts112 symbols
test/util/SpecHelper.ts47 symbols
src/event.ts34 symbols
src/optional.ts31 symbols
src/internal/source.ts27 symbols
performance/PerformanceTestCases.ts27 symbols
src/_.ts26 symbols
src/when.ts20 symbols
src/internal/updatebarrier.ts19 symbols
performance/MemTestHelper.ts17 symbols
src/scheduler.ts13 symbols
src/internal/dispatcher.ts13 symbols

Dependencies from manifests, versioned

@rollup/plugin-replace2.3.3 · 1×
@types/bluebird3.5.26 · 1×
@types/chai4.1.7 · 1×
@types/mocha7.0.2 · 1×
@types/node14.6.0 · 1×
@types/sinon9.0.0 · 1×
@types/zen-observable0.8.0 · 1×
benchmark2 · 1×
bluebird3 · 1×
browserify16.2.3 · 1×
browserstack-runner0.9 · 1×
chai4 · 1×

For agents

$ claude mcp add bacon.js \
  -- python -m otcore.mcp_server <graph>

⬇ download graph artifact