Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

7.0 bug, second switchMap gets executed hundreds of time initially #6167

Closed
Evertt opened this issue Mar 22, 2021 · 3 comments
Closed

7.0 bug, second switchMap gets executed hundreds of time initially #6167

Evertt opened this issue Mar 22, 2021 · 3 comments

Comments

@Evertt
Copy link

Evertt commented Mar 22, 2021

Bug Report

The code:

import { defer, BehaviorSubject, of } from "rxjs";
import { shareReplay, switchMap } from "rxjs/operators";

const oneRandomNumber = defer(() => of(Math.floor(Math.random() * 20)));

const cache = { key: oneRandomNumber.pipe(shareReplay(1)) };

export const swr = () =>
  new BehaviorSubject(null).pipe(
    switchMap(() => cache.key),
    switchMap((number) => {
      console.log(number); // This logs hundreds of times on initial load
      return cache.key;
    })
  );

swr().subscribe();

Link to demo

Current Behavior
I expect to see one log of a random number between 0 and 19.

Expected behavior
I see hundreds of logs those random numbers.

@josepot
Copy link
Contributor

josepot commented Mar 22, 2021

I've narrowed down the issue. It's a problem with shareReplay with sync sources. When a second subscription gets created right after the first subscription emits the first value and before it completes, then it re-subscribes to the source, rather than returning the latest emitted value.

For instance, the following snipped works perfectly fine (because the source is async):

import { Observable } from "rxjs";
import { shareReplay } from "rxjs/operators";

const source$ = new Observable(subscriber => {
  const token = setTimeout(() => {
    subscriber.next(Math.random());
    subscriber.complete();
  }, 0);

  return () => {
    clearTimeout(token);
  };
});

const randomNumber$ = source$.pipe(shareReplay(1));

randomNumber$.subscribe(x => {
  console.log(x);
  randomNumber$.subscribe(x => {
    console.log(x);
  });
});

However, this would likely hang your browser:

import { Observable } from "rxjs";
import { shareReplay } from "rxjs/operators";

const source$ = new Observable(subscriber => {
  subscriber.next(Math.random());
  subscriber.complete();
});

const randomNumber$ = source$.pipe(shareReplay(1));

randomNumber$.subscribe(x => {
  console.log(x);
  randomNumber$.subscribe(x => {
    console.log(x);
  });
});

And this also works fine (because the second subscription gets created after complete):

import { Observable } from "rxjs";
import { shareReplay } from "rxjs/operators";

const source$ = new Observable(subscriber => {
  subscriber.next(Math.random());
  subscriber.complete();
});

const randomNumber$ = source$.pipe(shareReplay(1));

randomNumber$.subscribe({
  next: x => {
    console.log(x);
  },
  complete: () => {
    randomNumber$.subscribe(x => console.log(x));
  }
});

@josepot
Copy link
Contributor

josepot commented Mar 22, 2021

I went to fix this, but it turns out that @cartant beat me to it on #6151 .

@cartant
Copy link
Collaborator

cartant commented Mar 22, 2021

Closing as a (far-from-obvious) dupe of #6144.

@cartant cartant closed this as completed Mar 22, 2021
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

3 participants