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();
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
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
All done, this is what you'd see in the console.
- 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})
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)
This is just amazing! ⭐
really amazing!