DEV Community

Cover image for Effortless RxJS Debugging
Ankit Singh
Ankit Singh

Posted on

Effortless RxJS Debugging

It's no secret that Observables are powerful, and RxJS makes them even more powerful and fun with its magical collection of operators.

I even built a State-Management library (ActiveJS) based on RxJS Observables.

PROBLEM

Sometimes piped streams built with multiple operators can become very confusing, and debugging Observable streams isn't actually fun. The stack-trace is of no use, and conventional debugging can be too tedious when the debugger goes through all the internal code.

More often than not we resort to the one and only savior of heathens and believers alike, the tap operator.
We've all done this tap(v => console.log('here', v)) ;)

SOLUTION

What if I told you that we can visualize every step of the Observable stream down to every operator and it's output, including subscription and completion, with a single utility function.

That's exactly what RxJS-Debug provides, a single function for automated RxJS visualization.

Let's assume we have an Observable stream like this:

const source$ = timer(1000, 4000); // emits a number after every 4 seconds
source$.pipe(
    map(x => x + 5), // add 5
    take(2), // complete the stream after 2 values
    switchMap(x => of(x * 2)) // multiply by 2, (can be an API call)
  )
  .subscribe();
Enter fullscreen mode Exit fullscreen mode

Visualizing this stream is as easy as wrapping our source$ Observable with the utility function $D provided by RxJS-Debug.

// wrap the Observable with $D to visualize it
const debugSource$ = $D(source$);
// debugSource$ is a copy of the original Observable, but with logging-enabled
Enter fullscreen mode Exit fullscreen mode

Now we just have to use the debugSource$ instead of source$.

debugSource$
  .pipe(
    map(x => x + 5),
    take(2),
    switchMap(x => of(x * 2))
  )
  .subscribe(); // activate the stream
Enter fullscreen mode Exit fullscreen mode

All done, this is what you'd see in the console.

rxjs-debug

  • we can see when the subscription started
  • we can see when every operator is executed, and
  • what the output of that operator was, and
  • what is the position of that operator in the pipe (e.g: 1, 2, 3 etc.)
  • we can also see when the stream restarts after a new value is emitted by the source$ Observable
  • we can see the count of how many times the source emitted
  • we can see the count of how many times an operator gets executed
  • we can see when the stream reaches the end of piped operators, and
  • we can also see when the Observable completes

All that with a single function; isn't that nice.

But hold on, why is there a delay between the operators, well I skipped over configuration options that you can pass to the $D.

This is the actual configuration that was used to produce the above GIF (or GIF, as you prefer).

// wrap the Observable with $D to visualize it
const debugSource$ = $D(source$, {id: 'Special', addDelay: 500})
Enter fullscreen mode Exit fullscreen mode

The id is an optional identifier to easily identify the logs related to the Observable, otherwise, an incremental numeric id is assigned automatically.

The addDelay flag adds a fixed delay before every operator supplied to the pipe, it gives you time to understand the outputs and execution flow at a more manageable pace.

There's one other option hideOutputs to hide the outputs of operators to reduce noise in the console logs, it can be helpful when you're only interested in the flow of execution, not the actual value.

That's it, folks, for now.
Please let me know if you have any questions, suggestions, or feedback.

Cheers

🤾‍♂️ RxJS-Debug Playground
💻 RxJS-Debug GitHub Repo (drop a ⭐ maybe :)

Top comments (2)

Collapse
 
eneajaho profile image
Enea Jahollari

This is just amazing! ⭐

Collapse
 
xuyanquan profile image
Henry

really amazing!