Debugging RxJS, Part 1: Tooling

Image for post
Image for post
Photo by Adam Sherez on Unsplash

I’m an RxJS convert and I’m using it in all of my active projects. With it, many things that I once found to be tedious are now straightforward. However, there is one thing that isn’t: debugging.

The compositional and sometimes-asynchronous nature of RxJS can make debugging something of a challenge: there isn’t much state to inspect; and the call stack is rarely helpful. The approach I’ve used in the past has been to sprinkle operators and logging throughout the codebase — to inspect the values that flow through composed observables. For a number of reasons, this approach is not one with which I’ve been satisfied:

  • I always seem to have to add more logging, changing the code whilst debugging it;
  • once debugged, I either have to remove the logging or put up with spurious output;
  • conditional logging to avoid said output looks pretty horrid when slapped in the middle of a nicely composed observable;
  • even with a dedicated operator, the experience is still less than ideal.

Recently, I set aside some time to build a debugging tool for RxJS. There were a number of features that I felt the tool must have:

  • it should be as unobtrusive as possible;
  • it should not be necessary to have to continually modify code to debug it;
  • in particular, it should not be necessary to have to delete or comment out debugging code after the problem is solved;
  • it should support logging that can be easily enabled and disabled;
  • it should support capturing snapshots that can be compared over time;
  • it should offer some integration with the browser console — for switching debugging features on/off and for investigating state, etc.

And some more that would be nice to have:

  • it should support pausing observables;
  • it should support modifying observables or the values they emit;
  • it should support logging mechanisms other than the console;
  • it should be extensible;
  • it should go some way towards capturing the data required to visualize subscription dependencies.

With those features in mind, I built .

Core Concepts

introduces a operator that associates a string tag with an observable. The operator does not change the observable’s behaviour or values in any way.

The operator can be used alone — — and the other methods can be omitted from production builds, so the only overhead is the string annotations.

Most of the tool’s methods accept matchers that determine to which tagged observables they will apply. Matchers can be simple strings, regular expressions or predicates that are passed the tag itself.

When the tool is configured via a call to its method, it patches so that it is able to spy on all subscriptions, notifications and unsubscriptions. That does mean, however, that only observables that have been subscribed to will be seen by the spy.

exposes a module API that is intended to be called from code and a console API that is intended for interactive use in the browser’s console. Most of the time, I make a call to the module API's method early in the application’s start-up code and perform the remainder of the debugging using the console API.

Console API Functionality

When debugging, I usually use the browser’s console to inspect and manipulate tagged observables. The console API functionality is most easily explained by example — and the examples that follow work with the observables in this code:

The console API in is exposed via the global.

Calling will display a list of all tagged observables, indicating their state (incomplete, complete or errored), the number of subscribers and the most recently emitted value (if one has been emitted). The console output will look something like this:

Image for post
Image for post

To show the information for only a specific tagged observable, a tag name or a regular expression can be passed to :

Image for post
Image for post

Logging can be enabled for tagged observables by calling :

Image for post
Image for post

Calling with no arguments will enable the logging of all tagged observables.

Most methods in the module API return a teardown function that can be called to undo the method call. In the console, that’s tedious to manage, so there is an alternative.

Calling will display a list of the methods that have been called:

Image for post
Image for post

Calling and passing the number associated with the method call will see that call’s teardown function called. For example, calling will see the logging of the observable undone:

Image for post
Image for post

Sometimes, it’s useful to modify an observable or its values whilst debugging. The console API includes a method that functions in much the same way as the RxJS operator. It’s implemented in such a way that calls to the method will affect both current and future subscribers the to tagged observable. For example, the following call will see the observable emit — instead of or :

Image for post
Image for post

As with the method, calls to the method can be undone:

Image for post
Image for post

Being able to pause an observable when debugging is something that’s become almost indispensable, for me. Calling will pause a tagged observable and will return a deck that can be used to control and inspect the observable’s notifications:

Image for post
Image for post

Calling on the deck will display the whether or not the observable is paused and will display the paused notifications. (The notifications are RxJS instances obtained using the operator).

Image for post
Image for post

Calling on the deck will emit a single notification:

Image for post
Image for post

Calling will emit all paused notifications and will resume the observable:

Image for post
Image for post

Calling will see the observable placed back into a paused state:

Image for post
Image for post

It’s easy to forget to assign the returned deck to a variable, so the console API includes a method that behaves in a similar manner to the method. Calling it will display a list of the calls:

Image for post
Image for post

Calling it and passing the number associated with the call will see the associated deck returned:

Image for post
Image for post

Like the and calls, the calls can be undone. And undoing a call will see the tagged observable resumed:

Image for post
Image for post

Hopefully, the above examples will have provided an overview of and its console API. The follow-up parts of Debugging RxJS will focus on specific features of and how they can be used to solve actual debugging problems.

For me, has certainly made debugging RxJS significantly less tedious.

More Information

The code for is available on GitHub and there is an online example of its console API.

The package is available for installation via NPM.

For the next article in this series, see Debugging RxJS, Part 2: Logging.

This post is also published on my personal blog: ncjamieson.com.

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

Get the Medium app

A button that says 'Download on the App Store', and if clicked it will lead you to the iOS App store
A button that says 'Get it on, Google Play', and if clicked it will lead you to the Google Play store