What Is Reactive Programming and how Does It Work?

gwagner's picture

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.

Figure 1: A Tweet event has a user (its author) as a participant.

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.

Figure 2: HTML elements participate as targets in mouse events.

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.

Category: