Submitted by gwagner on
The term "reactive programming" refers to a programming style based on handling "events" of all sorts. App developers are familiar with simple user interface events, such as mouse clicks, and know how to deal with them using their favorite programming language. But there are also other kinds of events an app may have to deal with, such as a state change of an object, an incoming message, the completion of an asynchronuous operation or an exception/error event.
The goal of reactive programming is to provide a systematic way how to deal with all kinds of events in an app. This is supported by a number of relatively new libraries for various programming languages, including RxJS and Bacon.js for JavaScript, Rx.NET for C# and RxJava for Java.
For understanding the potential of reactive programming, one has to understand the semantics of events and how they differ from objects.
In the following sections, we assume that the reader is familiar with basic concepts of object-oriented programming (objects, classes, properties, methods, class hierarchies, inheritance) and how they are visualized in UML class diagrams, and knows how to deal with objects in JavaScript.
Objects and Events
In the real world, we have to deal with objects (such as balls or cars) as well as with events (such as a goal in a soccer game or a car accident). The same holds for the digital world, where we deal with programming objects and programming events, which may represent real-world objects and real-world events. Digital events, such as a Twitter tweet, an incoming HTTP response message or a user interface event (like a mouse click), are a special kind of real-world events. They are typically represented in an app by corresponding programming events that can be "caught" and processed by the app.
While objects exist in time, having a life span, events happen, having an occurrence time and possibly changing the state of affected objects. Therefore, in an object-oriented programming language like JavaScript, real-world objects are represented by persistent programming objects, which are stored in databases, while real-world events are represented by transient programming objects that are typically destroyed after being processed (events are also said to be "consumed").
There is a special connection between objects and events:
objects participate in events by playing a
specific role. This includes the case where an object is affected by an
event in the sense that its state is changed through the event. For
example, one or more cars participate in a car accident, and their state
may be changed through the accident since they may be damaged. Also, a
Twitter user may participate in a tweet as its user/author, as shown in
the UML class diagram in Fig. 1, where both the object type
User
and the event type Tweet
are modeled as
classes.
As we can see in the class diagram in Fig. 1, the object type
User
has two properties: id
and
name
, while the event type Tweet
has, in
addition to the two properties id
and text
,
also a reference property user
, which represents the
association of a Twitter user as the author of a tweet.
At runtime, an event is represented by a special programming object instantiating an event class representing its type. Like any other programming object, a programming event may have property value slots, like the Tweet event {id: 4711, user: {id: 6253282, name:"realDonaldTrump"}, text:"..."}, according to the properties defined by its type, possibly including the event's occurrence time and references to the objects participating in it.
Events normally occur "asynchronously", that is, we don't know their occurrence time in advance even when we expect them to occur soon, and we don't want to wait for them, but rather do some useful things meanwhile.
Reacting to User Interface Events with Event Handlers
In this section, we summarize how to deal with user interface events in JavaScript.
The most common type of events in an app are user interface events arising from user actions. In a web app, user interfaces are implemented with HTML, CSS and JavaScript. User actions, such as a mouse click on a button or text input in an input field, are represented as "DOM events" in the JavaScript environment of a browser (recall that the acronym DOM stands for "Document Object Model", which is a standard that essentially defines a set of interfaces for dealing with HTML elements as JavaScript objects). For defining a variety of user interface event types, the DOM Events standard defines a hierarchy of interfaces that are implemented by corresponding JavaScript classes.
An important type of DOM events are mouse events, which are targeted
towards a specific HTML element (their "target"). As shown in Fig. 2,
click
and dblclick
events are mouse events and
mouse events are DOM events. This is the subclass/inheritance hierarchy
expressed in the class diagram of Fig. 2 with arrows having a large
arrowhead. It implies that both click
and
dblclick
events inherit the screen coordinate properties
screenX
and screenY
from the class
MouseEvent
, and the target
property
and preventDefault()
method from
DOMEvent
.
Processing an event means to react to it. The most widely used standard
API for reacting to events is the "DOM Events" API, which
defines how to register event handlers (or 'callbacks') in the form of
event listeners and how to access event properties. An event
handler is a procedure that defines a reaction to events of a certain
type. For instance, we could define an event handler that shows the
hidden content of a special web page section, containing an optional
explanation, and that is triggered whenever the user clicks on the
section heading. Here, the triggering event type would be "click" events
on h1
elements within section
elements.
An event listener associates an event handling procedure ("event handler") with a DOM event type and an HTML element as the observer of events. Since the DOM defines a "bubbling" logic for catching events, we have to distinguish between the target element, where the event occurs, and the observer element, where the event is caught and processed.
Event bubbling means that when an event occurs at some target element,
the browser first executes any event listeners defined for this type of
event at the target element. The event is then propagated upwards the
HTML element hierarchy via all ancestor elements to the root element
html
, and on its way further event listeners may be
executed, if there are any. This mechanism allows to catch events either
at the target element where they occur, or higher up in the HTML element
hierarchy by defining an event listener at some ancestor element playing
the role of an observer.
The following JS function is an example of an event handler. It
displays an event's type (e.type
), its target
(e.target
), and its observer (e.currentTarget
)
within a user interface element with ID "message-panel":
function handleEvent(e) { msg = "Event type: " + e.type + ", "; msg = msg + ", target: " + e.target.nodeName; msg = msg + "observer: " + e.currentTarget.nodeName; document.getElementById("message-panel").innerHTML += "<p>" + msg + "</p>"; }
Notice that the events, which trigger the execution of an event handler
like handleEvent(e)
, are passed to the event handler as the
value of its event parameter e
.
For defining event listeners with handleEvent
we can use a
procedure init
that is executed immediately after loading
the web page. In this procedure, we define three event listeners:
function init() { var b = document.body; var t1 = document.getElementById("t1"); b.addEventListener("click", handleEvent); b.addEventListener("dblclick", handleEvent); t1.addEventListener("click", handleEvent); } // execute the init function when document has been loaded window.addEventListener("load", init);
With these event listeners we catch click
events at the
body
element and at an element with ID "t1", as well as
dblclick
events at the body
element.
Event Sources and Event Streams
In general, a program execution environment, like a web browser or NodeJS, defines a set of typed event sources. In the case of a web browser, they include various types of user interface events and events related to HTTP messages in the context of the XmlHttpRequest (XHR) API.
Any event source can be considered as the basis of an event stream, which is simply the sequence of occurrences of events of the type defined by the event source. For instance, the Twitter tweets of Donald Trump define an event stream. Also the successive click events during a HTML page view session define an event stream. An event occurrence often comes with some event data. This is why event streams are sometimes also called "asynchronous data streams". However, since the distinction between objects and events is helpful for understanding the semantics of reactive programming, we prefer the name "event stream" over "asynchronous data stream".
Reacting to Possibly Complex Events with Event Streams
The goal of reactive programming libraries like Rx is to provide data structures, like event streams, and procedures, like stream transformations and compositions, that allow dealing with different kinds of events in a uniform way, including representing and processing complex events.
A simple event stream is bound to a single event source, such as to a DOM event or to the completion of an asynchronuous operation (a "promise"). Thus, when using a reactive programming library, we can define an event stream instead of an event listener. Event streams allow a more uniform handling of different kinds of events. They also allow to handle complex events.
Derived event streams can be defined on the basis of simple streams representing event sources or other derived streams, with the help of transformations such as event filters and mappings, or with the help of composition operations such as merging two streams.
For instance, we could define a derived tweet stream by filtering Donald Trump's tweets using the keywords "Hillary" and merging the resulting stream with a tweet stream by Hillary Clinton filtered with the key word "Donald". Notice that event stream merging corresponds to a disjunction of event types. Thus, the complex event type "Trump tweet about 'Hillary' or Clinton tweet about 'Donald'" can be handled by defining a corresponding event stream.
Summary
Reactive programming libraries like Rx support processing different kinds of simple and complex events in a uniform way with the help of event streams. While they increase developer productivity when dealing with complex events, they are also useful, but not required, for dealing with simple events, due to their elegant syntax.