ReactiveX / ReactiveX/rxjs

Suggested replacements for deprecated multicasting operators do not behave equivalently

Open
#6,452 6 comments 4 reactions 0 assignees View on GitHub

Nobody has claimed this yet.

Dominant language
TypeScript
Stars
31.7k
Forks
3k
PR merge metrics
No merged PRs in 30d

Description

Bug Report

Current Behavior
The replacements suggested for the deprecated multicasting operators in the migration guide https://rxjs.dev/deprecations/multicasting and on the individual operator pages do not behave exactly like the deprecated operators. For the publishReplay case a detailed discussion is here: https://github.com/ReactiveX/rxjs/discussions/6438, but this issue is not limited to publishReplay.

Expected behavior
Using a suggested replacement behaves exactly like the replaced operators or it is documented explicitly when they don't (and maybe also why they shouldn't).

Reproduction

(leaving out imports for brevity)

const tick$ = timer(1_000).pipe(
  publishReplay(1),
  refCount()
);

does not behave equivalently to the suggested refactor:

const tick$ = timer(1_000).pipe(
  share({
    connector: () => new ReplaySubject(1),
    resetOnError: false,
    resetOnComplete: false,
    resetOnRefCountZero: false,
  })
);

demo: https://codesandbox.io/s/frosty-sun-2tv3h?file=/src/index.ts

a truly equivalent replacement would be:

const tick$ = timer(1_000).pipe(
  (source$) => {
    const subject = new ReplaySubject(1);

    return source$.pipe(
      share({
        connector: () => subject,
        resetOnError: false,
        resetOnComplete: false,
        resetOnRefCountZero: true,
      }),
    );
  }
);

or if the suggested releaseConnectorOnRefCountZero config option is introduced:

const tick$ = timer(1_000).pipe(
  share({
    connector: () => new ReplaySubject(1),
    resetOnError: false,
    resetOnComplete: false,
    resetOnRefCountZero: true,
    releaseConnectorOnRefCountZero: false,
  })
);

Environment

  • Runtime: any
  • RxJS version: 7

Possible Solution

  • Change or extend the migration guide to include truly equivalent replacements or at least document the changed behaviors.
  • Parameterize the test cases of deprecated operators to execute them with the suggested replacements
  • Introduce a new option to ShareConfig (and maybe also ConnectableConfig & ConnectConfig): releaseConnectorOnRefCountZero: boolean | (() => ObservableInput<any>) = true. this will be used to control if and when to create a new connector, when no one is subscribed anymore. a memory-optimal time-based caching behavior on cold sources can then be achieved by:
    const tick$ = timer(1_000).pipe(
      share({
        connector: () => new ReplaySubject(1),
        resetOnError: false,
        resetOnComplete: false,
        resetOnRefCountZero: true,
        releaseConnectorOnRefCountZero: () => timer(5000),
      }),
    );
    

Contributor guide

Open the contributing guide

First steps

  1. Read the whole issue, then the project's contributing guide.
  2. Comment on the issue to say you are picking it up — it saves two people doing the same work.
  3. Fork the repository and make your change on a branch.
  4. Open a pull request that references the issue number.

Research direction

Start with the multicasting migration guide at rxjs.dev/deprecations/multicasting and the individual operator pages, then review discussion #6438 and the deprecated-operator test cases. Compare each suggested replacement with the deprecated operator's behavior, especially publishReplay and share configuration. Done means the replacements are equivalent or their differences and rationale are documented, with tests covering the comparisons.

Written by the indexing model from the issue text.

Assessment

Tech stack
typescript
Domain
api, documentation
Issue type
Bug
Difficulty
4/5
Estimated time
3-5 days
Activity status
Stale
Clarity
Mostly clear
Newbie friendliness
35/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.