In what way can a piped operator in rxjs be influenced by the value returned by a subsequent operator?

When attempting to examine values between operators in an rxjs pipe, I decided to use tap to log them to the console.

I added two taps, one before a map operator used for sorting an Array, and one after. Surprisingly, both taps logged the same sorted Array, instead of displaying the original order followed by the sorted one.

This indicates that the sorting done in the map operator impacts the observable used by the preceding tap operator. However, the operators' functions appear to execute sequentially as expected, since the log from the second map is displayed between the taps' logs.

Here is my code (I plan to specify the Observable Array's type properly later):

public getJSONData(): Observable<any[]> {
    return this.http.get(this.poisJSONPath).pipe(
      map((featureCollection: any) => featureCollection.features),
      tap((features) => console.log(features)),
      map((features: any) => {
        console.log('this is logged in between as expected');
        return features.sort((a, b) =>
          a.properties.name > b.properties.name ? 1 : -1
        );
      }),
      tap((features) => console.log(features))
    );
  }

I suspect there may be a critical aspect of how rxjs pipes operate that I'm overlooking. In every other example I've seen, tap behaves as I anticipate. Can anyone point out what I might be missing?

Answer №1

The most straightforward explanation is that the array is already organized.


Upon closer inspection, when I execute this code:

of({
  features: [3,4,7,1,3,8]
}).pipe(
  pluck('features'),
  tap(console.log),
  map(features => {
    console.log('this is logged in between as expected');
    return features.sort(
      (a, b) => a > b ? 1 : -1
    );
  }),
  tap(console.log)
).subscribe(console.log);

This is what I see in the output:

[3,4,7,1,3,8]
this is logged in between as expected
[1,3,3,4,7,8]
[1,3,3,4,7,8]

Therefore, it could be related to the environment where you are running the code.

If, for instance, console.log is buffered, then maybe the array has been altered in memory before it's actually printed, even though the order of printing remains intact.


An Important Point

RxJS does not provide any assurances about the state of memory. Hence, it is advisable to use pure, non-mutating functions so that your program logic becomes easier to understand.

Take into account this example:

const delayedLog = val => setTimeout(() => console.log(val), 1000);

of({a: 5}).pipe(
  tap(delayedLog),
  map(input => {
    delayedLog("this is logged in between as expected");
    input.a++;
    return input;
  }),
).subscribe(delayedLog);

output:

{"a":6}
this is logged in between as expected
{"a":6}

The value {"a":6} is printed two times although initially a = 5 when the first delayed log is triggered. This happens because the value changes in memory prior to the console reading the value from memory.

Vs

const delayedLog = val => setTimeout(() => console.log(val), 1000);

of({a: 5}).pipe(
  tap(delayedLog),
  map(input => {
    delayedLog("this is logged in between as expected");
    return { a: input.a + 1 }
  }),
).subscribe(delayedLog);
{"a":5}
this is logged in between as expected
{"a":6}

In this case, we obtain the anticipated result since we do not alter the object directly; instead, we return a new object with the incremented value.


A Starting Point for Investigation

Try sorting a copy of the array and check if that resolves the issue for you.

public getJSONData(): Observable<any[]> {
  return this.http.get(this.poisJSONPath).pipe(
    pluck('features'),
    tap(console.log),
    map((features: any[]) => {
      console.log('this is logged in between as expected');
      return [...features].sort((a, b) =>
        a.properties.name > b.properties.name ? 1 : -1
      );
    }),
    tap(console.log)
  );
}

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

A guide to implementing angularjs app.service and $q in typescript

I am fairly new to TypeScript and AngularJS and I am struggling to find the correct answer for my issue. Below is the relevant code snippet: export class SidenavController { static $inject = ['$scope', '$mdSidenav']; constructor(p ...

Learning how to smoothly navigate to the top of a page when changing routes in Angular using provideRouter

Is there a way to make the Angular router automatically scroll to the top of the page when the route changes? In my previous setup using NgModules, I could achieve this with: RouterModule.forRoot(appRoutes, { scrollPositionRestoration: 'top' }) ...

Tips on using the `IsEqual` function to develop a tool that verifies the similarity of different data types

When working with TypeScript, I often utilize the type-fest npm package in my coding. For documentation purposes, I sometimes like to assert that two types are either equal or unequal. Here is an example: const b: IsEqual<{a: 1}, {a: 1}> = true; con ...

Why is the Ionic 3 HTTP request malfunctioning on iOS, while functioning correctly on Android?

I have developed an application using Ionic 3 that connects to a Spring Boot API for user authentication. The Spring Boot application is hosted on AWS and the functionality works perfectly on Android devices. However, when testing it on iOS, I encountered ...

Angular template error: 'Component' type does not have the specified property

I am facing an issue with importing an array from a file named platforms.ts. The array is declared as shown below: export const platforms: Platforms[] = [ { name: 'Instagram', value: 'instagram' }, { name: 'Facebo ...

Limit the types of components allowed as children in a React component by utilizing TypeScript

I've been struggling with this question for a while, searching through answers but still unable to get my code to work. Here's the issue at hand. Within my code, I have a component called Steps which acts as a wrapper. I also have multiple Step ...

Transform an Angular 2 application to seamlessly incorporate an SDK

I have been working on an Angular 2 application and I am curious if it is feasible to transform this into an SDK that can be easily integrated into other applications by simply adding script tags in their headers. If this conversion is not achievable, co ...

Using ngModel in multiple mat-select elements in Angular 2/4

I have a mat-select dropdown list that allows multiple selections and I am using NgModel to keep track of the user's selected values. The issue arises when I navigate away from the page and return, the user's selected values are not preserved in ...

The properties are not found in the type 'Observable<Todo[]>'

I'm currently following a YouTube tutorial and I've hit a roadblock trying to figure out where the error is originating from? Error Message: Type 'Observable' is missing properties such as length, pop, push, concat, and 25 more.ts(2740 ...

Using Typescript with NodeJs

As I work on my app.ts file, I prefer using this approach. However, I’ve been encountering some problems with .d.ts imports, which are preventing me from accessing the full API of express. The main issue is that in Webstorm2016 (I haven’t tested it on ...

The Angular Syncfusion schedule is unable to call upon an object that may potentially be 'undefined'

Currently, I am developing an application using Angular Syncfusion to allow users to view and book appointments. I found a helpful resource at the following link: Below you can find the code snippet I have been working on: <ejs-schedule #scheduleObj ...

Dynamic Text Labels in Treemap Visualizations with Echarts

Is it possible to adjust the text size dynamically based on the size of a box in a treemap label? I haven't been able to find a way to do this in the documentation without hardcoding it. Click here for more information label: { fontSize: 16 ...

Unable to run `create-react-app` with the `--template typescript` option

Every time I try to execute the following command: npx create-react-app my-app --template typescript only a JavaScript project is created, without any .tsx files. After consulting the CRA's TypeScript guide, it appears that the command requires Node ...

switchMap: Triggering multiple requests simultaneously (2)

Currently, I am utilizing Angular 2 RC-4 and facing an issue where a network request is being triggered twice whenever there is a change in the input box. This is what my code looks like: component.ts this.term = new Control(); this.suggestions = this. ...

Is there a way to expand the return type of a parent class's methods using an object

Currently, I am enhancing a class by adding a serialize method. My goal is for this new method to perform the same functionality as its parent class but with some additional keys added. export declare class Parent { serialize(): { x: number; ...

How can you dynamically disable the submit button on a form in Angular 7+ based on the form's current status?

Let's consider a scenario with a button in our code: <button type="button" class="menu-button" [disabled]="isInvalidForm()">Save</button isInvalidForm() { console.log('I am running!'); return this.nameValidator.errors || this.last ...

Curious about the missing dependencies in React Hook useEffect?

I'm encountering the following issue: Line 25:7: React Hook useEffect has missing dependencies: 'getSingleProductData', 'isProductOnSale', and 'productData'. Either include them or remove the dependency array react-hoo ...

What is the best way to retrieve an object within a class?

Exploring a Class Structure: export class LayerEditor { public layerManager: LayerManager; public commandManager: CommandManager; public tools: EditorTools; constructor() { this.commandManager = new CommandManager(); this.lay ...

"Learn the trick of converting a stream into an array seamlessly with RxJs.toArray function without the need to finish the

In order to allow users to filter data by passing IDs, I have created a subject that can send an array of GUIDs: selectedVacancies: Subject<string[]> = new Subject(); selectedVacancies.next(['a00652cd-c11e-465f-ac09-aa4d3ab056c9', ...

Ways to fix the perpetual cycle in adal-angular4 following authentication redirect

I have been working on an Angular 8 application that utilizes Microsoft Azure Active Directory authentication with adal-angular4. I've successfully set up an ASP.NET Core API linked to a client app on Azure. To configure Active Directory, I referred ...