Debugging RxJs

Angular: The Full Gamut Edition

Charlie Greenman
June 21, 2020
5 min read

Before We Start

One of the more difficult things to master when learning RxJS is learning how to debug a stream. Within regular Typescript, debugging is relatively easy. I find myself alternating between two different methods when following this approach: 1. Very simply using console.log 2. Using DevTool Sources


Using Console.log

In a regular Typescript/Javascript setting a console.log is a relatively simple concept. We show(,or log out), whatever the current status is of our code at a given time. So for instance, let’s say that we wanted to create a piece of code that takes the first and last name, and combines(proper software term is concatenates) them together. Something like this:

combinedName(firstName: string , lastName: string): string { 
  return firstName + lastName;
}

So the developer, due to the low level of the logic of this function, mistakenly forgets to add white space in between. He decides to console out the function, before applying the logic directly to the application, to make sure it works as expected. So the developer does something like this(assuming we are talking about a method within a service):

console.log(this.combinedName('James', 'Harden));

The developer is expecting it to look like ‘James Harden’, but instead, the developer finds out that it is ’JamesHarden’. The developer immediately realizes that there is a missing white space in between the two words, and edits the function, so that it works as expected:

combinedName(firstName: string , lastName: string): string { 
  return firstName + ' ' + lastName;
}

This individual then goes back, and refreshes the page where the console.log was, and finds out that it now looks like:

'James Harden'

In practice, something like this should ideally be tested using unit tests. However, many enterprise applications unfortunately are not built in the optimal fashion for various reasons.


Using DevTool Sources

The only other tool that I usually have within my tool belt to dissect an error immediately is the developer tools within chrome. Any time that there is an error, there is a unique signature that your code will throw out. In a modern day application, this will happen even if it isn’t unique to your application, due to the number of libraries/frameworks we use. However, it can still be incredibly useful for hard to find bugs, if you are willing to dig deep enough into the call stack.

This article will not discuss this technique in-depth. Feel free to check out the video series by egghead.io here, for more details. I just wanted to bring this up, as an integral part of day to day debugging.


Debugging within RxJS — The Dilemma

The immediate issue with debugging RxJS, coming from a traditional setting, is that it’s only the final result that is outputted, but that is not the case. One might think, it is therefore only the final result that can be debugged. For instance, you might have a stream of numerous chained events like so:

this.companies$ = searchBy$.pipe(
  debounceTime(300),
  distinctUntilChanged(),
  startWith(''),
  switchMap((criteria:string) => {
    const request$ = this.companyService.searchCompanies(criteria);
    return !criteria.length ? of([]) : request$
  })
);

In the above, only in the switchMap, or in a subscribe will we be able to tap into the result that we are looking for. For instance, let’s go through the potential bugs that might happen with the above stream: 1. The result might not appear because it is within the debounceTime; 2. It might not have changed from the previous result, i.e. distinctUntilChanged, and therefore is not triggered. 3. The companyService.searchCompanies request might be erroring out, or there might be some internal logic to the companyService.searchCompanies request making something error out.

So, of course, we need a similar way to console out our results along the line of the stream, to figure out where our error is happening.


Using Tap

tap is RxJS’s way of taking the current value and outputting it. tap is colloquially referred to as a “side-effect”. A piece of logic within our RxJS stream, that does not directly output to the stream’s final output. It is commonly used in real-life code, by taking the value returned by an HTTP/GraphQL service, and passing data returned to a function/action. However, tap can also be used as a debugger. For instance, in the above code, let’s say we aren’t getting the result we wanted. We can insert tap as follows:

this.companies$ = searchBy$.pipe( 
  debounceTime(300),
  tap(result => console.log(result)), 
  distinctUntilChanged(), 
  startWith(''), 
  switchMap((criteria:string) => {
    const request$ = this.companyService.searchCompanies(criteria);
    return !criteria.length ? of([]) : request$ }
  )
);

In the above tap, (let’s imagine) a result is coming back from our console.log. In this scenario, we now know that it is not erroring out before debounceTime. Ok, so let’s move the tap down one more:

this.companies$ = searchBy$.pipe( 
  debounceTime(300), 
  distinctUntilChanged(),
  tap(result => console.log(result)), startWith(''),
    switchMap((criteria:string) => {
      const request$ = this.companyService.searchCompanies(criteria);
  return !criteria.length ? of([]) : request$ })
);

Here (let’s imagine, but using this example for the point of demonstration) we are no longer receiving the console.log that we want. This is/would be telling us that the reason we might not be receiving our result, is that the value is not being registered as anything distinct.

That’s it plain and simple. Use tap within your RxJS stream, in combination with console.log to be able to debug RxJS code.

I’ve seen one other article in particular claim use of a debug utility function that is only turned on in development mode. I personally am against such debugging patterns and feel it is an anti-pattern. I strongly believe that unit tests should be in place to prevent errors. Debugging should happen in one-off scenarios where unknown business cases or use cases cause an error, fixed and then reinforced with unit tests and integration tests to make sure error does not happen again.

That’s it, you are now a pro at debugging RxJS!!!

More articles similar to this

footer

Razroo is committed towards contributing to open source. Take the pledge towards open source by tweeting, #itaketherazroopledge to @_Razroo on twitter. One of our associates will get back to you and set you up with an open source project to work on.