Trouble arises with tap operator's output in RxJS

I'm puzzled by the results of the code below. According to the take(3) function, processing should stop after rendering 10, but I'm still seeing 5, 6, and 9 from the tap operator. Please see the output and code snippet below for reference.

  • Tapped value null
  • Tapped value 20
  • Rendering Item 20
  • Tapped value 15
  • Rendering Item 15
  • Tapped value 10
  • Rendering Item 10
  • Completed
  • Tapped value 5
  • Tapped value 6
  • Tapped value 9
of(null, 20, 15, 10, 5, 6, 9)
      .pipe(
        tap(val => console.log(`Tapped value ${val}`)),
        filterNil(),
        take(3)
      )
      .subscribe(
        item => console.log(`Rendering Item ${item}`),
        err => console.log(err),
        () => console.log('Completed')
      );
  }

const filterNil = () => (source: Observable<any>) =>
  new Observable(observer => {
    return source.subscribe({
      next(value) {
        if (value !== undefined && value !== null) {
          observer.next(value);
        }
      },
      error(error) {
        observer.error(error);
      },
      complete() {
        observer.complete();
      }
    });
  });

Answer №1

The conclusive response to this inquiry

You can find the accurate solution to this query in the provided link.

Initial answer - a straightforward explanation

The outcome you observe stems from the synchronous nature of your code, where the implicit unsubscribe after 3 emissions due to take(3) does not execute.

Consider this revised snippet

of(null, 20, 15, 10, 5, 6, 9)
      .pipe(
        delay(0),  // >>> introduce a delay
        tap(val => console.log(`Tapped value ${val}`)),
        filterNil(),
        take(3)
  )

Introducing a delay allows take to trigger an unsubscribe, resulting in the expected behavior.

A delve into detailed reasoning

Further investigation reveal nuances that challenge my previous explanation's simplicity.

Notably, filterNil() should function akin to filter(item => item !== null), a standard operator from rxjs/operators.

Substituting filter(item => item !== null) with filterNil() leads to contrasting outcomes

of(null, 20, 15, 10, 5, 6, 9)
      .pipe(
        tap(val => console.log(`Tapped value ${val}`)),
        filter(item => item !== null),
        take(3)
      )

      .subscribe(
        item => console.log(`Rendering Item ${item}`),
        err => console.log(err),
        () => console.log('Completed')
      );
  }

// console output:

Tapped value null
Tapped value 20
Rendering Item 20
Tapped value 15
Rendering Item 15
Tapped value 10
Rendering Item 10
Completed

These results indicate non-equivalence between filter(item => item !== null) and filterNil().

This discrepancy seems tied to the inner workings of Observable's subscribe method, influenced by the unique natures of filterNil and filter.

An illustration depicting the execution trace under filterNil:

https://i.sstatic.net/p2mgD.png

In contrast, the trace under filter is as shown here: https://i.sstatic.net/NAtTA.png

The differing attributes set within operator - null for filterNil and FilterOperator for filter - appear to dictate these variations. Understanding these intricacies warrants further exploration in a separate context.

Answer №2

It seems like your filterNil function is still running because it's a separate observable unaffected by the take operator. Remember, RxJS operators are executed in order. To fix this, consider moving the take to the beginning of the pipe chain or avoid creating a new observable inside your filter function.

// The position of operators matters
.pipe(
  take(3),
  tap(val => console.log(`Tapped value ${val}`)),
  filterNil()
)

// Alternatively, refactor the filterNil operator

const filterNilRefactor = () => {
  return source => source.pipe(filter((value) => value !== undefined && value !== null))
}

.pipe(
  tap(val => console.log(`Tapped value ${val}`)),
  filterNilRefactor(),
  take(3)
)

Similar questions

If you have not found the answer to your question or you are interested in this topic, then look at other similar questions below or use the search

When attempting to specify the path in the angular.json file, Angular encounters difficulty accessing Bootstrap from the node_modules directory

I have been attempting to integrate Bootstrap into my Angular project. Firstly, I used npm install --save bootstrap to add Bootstrap to my project. Following that, I installed jQuery as well. I then specified the path for Bootstrap. Displayed below is an ...

Transferring an array of interfaces between Angular 2 components

Encountering issues with passing an Array of data between components using @Input. Here is the code snippet: public ngOnInit() { this.applications = new Array<ApplicationEntryInterface>(); (...) let shortTermData = new ShortTermApplicationAdapte ...

Error: The Router.use() function expects a middleware function, but it received an undefined value with two middlewares involved

My ts-node app requires testing, and I encountered an issue while running the test. The app has routes that must pass through two middlewares: authorization and request validation. Here is the configuration for the routes: const router = express.Router(); ...

Checking the parameters passed to a function in Typescript: A step-by-step guide

Currently, I am working with Typescript and then transpiling my TS code into JavaScript. However, I have encountered an issue that I am struggling to resolve. The error message I am facing is as follows: Error Found in TypeScript on Line:2 - error TS230 ...

Getting TypeScript errors when incorporating a variant into a Material-UI button using a custom component

I have created a unique Link component inspired by this particular example. Take a look at the code below: import classNames from 'classnames'; import {forwardRef} from 'react'; import MuiLink, {LinkProps as MuiLinkProps} from '@ma ...

Using ES6 proxy to intercept ES6 getter functions

I have implemented a proxy to track all property access on instances of a class, demonstrated in the code snippet below: class Person { public ageNow: number; public constructor(ageNow: number) { this.ageNow = ageNow; const proxy = new Proxy( ...

Deliberately choosing not to fulfill the Bluebird Promise

Here is a piece of code that needs to call a callback which may return a promise. The goal is to resolve the promise and log an error if it fails, without the caller knowing about it or waiting for the promise to fulfill. However, not returning the promise ...

How to transfer an object between sibling components in Angular 4

Being new to Angular 2+, I understand that I may not be approaching this the correct way. My issue involves two sibling components. When clicking on a "link" in one component, it redirects to another controller. My goal is to pass an object to the componen ...

Using navigateByUrl() to pass a query parameter

When I click on an icon, I want to navigate to a new page. Here's the code snippet: this.prj = e.data.project_number; this.router.navigateByUrl('/dashboard/ProjectShipment/634'); Instead of hardcoding the query parameter 000634, I need ...

Encountering an uncaughtException: Error stating that the module '....nextserverapphomelibworker.js' cannot be located while attempting to utilize pino.transport in Next.js

I recently set up a Next.js project with typescript using create-next-app. Opting for Pino as the logging library, recommended by Next.js, seemed like the logical choice. Initially, when I utilized Pino without incorporating its transport functionality, e ...

Drizzle-ORM provides the count of items in a findMany query result

Hello there, I'm currently experimenting with the Drizzle ORM and imagine I have this specific query const members = await trx.query.memberTable.findMany({ with: { comments:true } }) I'm wondering how I can retrieve the total count of me ...

Checking conditions sequentially in Angular

I have a unique use case that requires me to verify certain conditions. If one condition fails, I should not proceed to the next one. Instead, based on the failed condition, I need to display a dialog with a title and description explaining what went wrong ...

Is there a more efficient method for casting the output of Object.keys() in Typescript?

Check out this code snippet: type myType = "a" | "b" | "c"; type myMappedType = { [str in myType]: number }; const values: myMappedType = { a: 1, b: 2, c: 3 }; const keys = Object.keys as <T>(o: T) => Extract& ...

What is the proper way to define a generic object interface in Typescript?

Within my code, I have constructs that resemble the following: { classNames: { foo: 'foo', .... bar: 'bar' }, method1: () => {....}, method2: () => {....}, stringKey1: 'stringKey1', ... stringK ...

Develop a tailored filter by utilizing the FilterPredicate to refine the DataSource

Currently, I have been storing all my data in a dataSource and using the standard filter for searches. However, I am now interested in performing searches using the "OR" operator. For example: {name : john, age : 25, work : driver} {name : peter, age : ...

Encountered an issue while attempting to integrate Nebular into my Angular application

As a newcomer to Angular, I decided to try installing Nebular using the command ng add @nebular/theme. However, I encountered an error in the process. Upon entering the command into my terminal, the following error message appeared: ? Which Nebular theme ...

What is the best way to retrieve the initial element in an array?

I am currently using this Rxjs: this.menuSubject$ .pipe(map((items) => items.filter((itm) => itm.active))) .subscribe((res) => console.log(res)); The variable this.menuSubject$ is defined as follows: public menuSubject$ = new Behavi ...

Issue with LokiJS: The DynamicView().branchResultSet method does not apply sorting to the collection

I'm encountering an issue retrieving data from a branchResultSet after applying the applySort function on the view. I'm utilizing this method to restrict the result set to 10 records for better performance, rather than fetching the entire dataset ...

Creating a TypeScript generic that can traverse the routes configuration tree while accumulating the path is a useful skill to have

I am struggling with defining a TypeScript generic to achieve a specific output. type Route = { path: string children?: readonly Route[] } const root = { path: "a", children: [ { path: "b" }, { path: "c", chil ...

What is the process of importing an IIFE-based JavaScript module into an Angular TypeScript application?

I have come across a third-party SDK that is structured as an oldschool IIFE based module. The code looks something like this: var ThirdPartySDK = (function() { var export = {}; // Adding some methods to export return export; })(); To use this SD ...