Before React fundamentally changed how we thought about UI state, and before RxJS became the undisputed heavyweight champion of asynchronous JavaScript, the frontend landscape of the early 2010s was a wild west of jQuery callbacks and unpredictable state mutations. Managing asynchronous data flows—user clicks, network requests, animations—often resulted in what developers affectionately called "callback hell."
It was during this era that Functional Reactive Programming (FRP) began to bleed into mainstream web development. Looking back, the early days of reactive programming were less about massive frameworks and more about fundamentally shifting how developers conceptualized time and data.
At the forefront of that shift in the JavaScript ecosystem was Bacon.js.
The Bacon.js Paradigm: Streams and Properties
Created by Juha Paananen, Bacon.js offered a clean, intuitive, and highly functional approach to handling events over time. What made Bacon.js truly special—and what many argue it did better than its early competitors—was its strict, semantic distinction between two core concepts:
- EventStream: A stream of discrete events happening over time (e.g., a button click, a key press). It has no concept of a "current" state; it only exists in the moment it fires.
-
Property: A reactive value that changes over time but always has a current state (e.g., the current text inside an input field, the current position of a mouse). You can think of a Property as a continuous value, often created by accumulating or mapping an
EventStream.
This mental model allowed developers to declare relationships between UI elements declaratively. Instead of writing imperative code saying "when this button is clicked, update this variable and redraw the screen," you could simply declare "the submit button's disabled state is a Property derived from the validity of the email input Property."
The Surprising Parallel: VHDL and Hardware Design
When you look deeply at the core philosophy of Bacon.js and early FRP, there is a fascinating, often overlooked parallel with VHDL (VHSIC Hardware Description Language), the language used to model electronic systems and digital circuits.
In traditional imperative programming, code executes line by line. But in hardware design (and in reactive programming), things happen concurrently, driven by signals and events.
-
Signals vs. Properties: In VHDL, you define
signalsthat connect different hardware components. When a signal's value changes, any component hooked up to that signal reacts instantly. This is practically identical to a Bacon.jsProperty. -
Clock Edges vs. EventStreams: VHDL relies heavily on clock edges (rising or falling) to trigger state changes in flip-flops or registers. These discrete temporal triggers are the hardware equivalent of a Bacon.js
EventStream. - Data Flow: Both paradigms rely on data flow graphs. You aren't writing a sequence of instructions; you are wiring up a circuit. In VHDL, you wire up logic gates; in Bacon.js, you wire up map, filter, and combine operations.
In many ways, early reactive programming was the software engineering world finally adopting the concurrency and data-flow principles that electrical engineers had been using in VHDL for decades.
Bacon.js vs. The Heavyweights: RxJS and RxCocoa
As the reactive paradigm gained traction, the ReactiveX (Rx) family took over. How does the pioneering Bacon.js compare to modern giants like RxJS and mobile equivalents like RxCocoa?
RxJS: The Standard
RxJS adopted the Observable as its single, unifying primitive. While incredibly powerful, this lack of built-in distinction between discrete events and continuous state initially made the learning curve steeper.
- In Bacon.js, getting a stateful property from an event stream is a simple
.toProperty()call. - In RxJS, achieving the same requires understanding multicasting,
Subjects, and specificallyBehaviorSubjector theshareReplayoperator. RxJS prioritized unified mathematical purity over Bacon's pragmatic semantic distinction.
RxCocoa: Reactive UI for Apple Ecosystems
RxCocoa brought the ReactiveX philosophy to iOS and macOS development. Much like how Bacon.js was used to tame the DOM, RxCocoa tames UIKit and AppKit. Interestingly, RxCocoa recognized the need for Bacon's "Property" concept when dealing with UIs. They introduced Drivers and Relays—specialized Observables that cannot error out and are guaranteed to deliver events on the main UI thread. A Driver in RxCocoa is conceptually very close to a Bacon.js Property designed specifically for safe UI binding.
A Comparative Look
| Feature | Bacon.js | RxJS | RxCocoa |
|---|---|---|---|
| Core Abstraction | EventStream & Property | Observable | Observable, Driver, Relay |
| Continuous State | Native Property type |
BehaviorSubject / Operators |
Driver / BehaviorRelay
|
| Primary Use Case | Taming early DOM events | Universal async/event handling | iOS/macOS UI Data Binding |
| Legacy | Pioneered semantic FRP in JS | The modern reactive standard | Standardized reactive mobile UI |
The Legacy of Bacon
Bacon.js may not be the framework you reach for in a new 2026 project, but its influence is undeniable. It taught a generation of JavaScript developers how to think in terms of time, data flows, and stateful properties, paving the way for the reactive architectures we take for granted today.
Top comments (0)