RxJS: Managing Operator State

Nicholas Jamieson
3 min readFeb 11, 2019
Photo by Victoire Joncheray on Unsplash

When pipeable operators were introduced in RxJS version 5.5, writing user-land operators became much simpler.

A pipeable operator is a higher-order function: a function that returns another function. And the function that is returned takes an observable and returns an observable. So, to create an operator, you don’t have to subclass Operator and Subscriber. You just write a function.

Simple.

However, there are situations in which you need to take some extra care. In particular, you need to be careful whenever your operator stores internal state.

An example

Let’s look at an example: a debug operator that logs received values and their indices to the console.

Our operator will need to maintain some internal state: the index — which will be incremented each time a next notification is received. A naive approach would be to store that state within the operator function. Like this:

However, this approach has a couple of problems that will effect surprising behaviours and hard-to-find bugs.

The problems

The first problem is that our operator is not referentially transparent. A function is referentially transparent when it’s possible to replace the function call with the value that would be returned without changing the program’s behaviour.

Let’s look at what happens when our operator’s returned value is used to compose several observables:

The program’s output is:

first use:
[0] 1
[1] 2
second use:
[2] 1
[3] 2

Well, that’s surprising. The index didn’t start at zero for the second observable.

--

--

Nicholas Jamieson

RxJS core team member; front-end developer; mentor; speaker; open-source contributor