Skip to content

Unsubscribe from a combineLatest doesn't unsubscribe from underlying observables #7318

Open
@RoqueIT

Description

@RoqueIT

Describe the bug

Since the version 7.x.x, the behavior seems to have changes in the unsubscription mecanism for combineLatest: unsubscribe from a combineLatest doesn't unsubscribe from underlying observables

Expected behavior

Unsubscribe from a combineLatest observable should unsubscribe from underlying observables

Reproduction code

import { describe, expect, jest, test } from '@jest/globals';
import { combineLatest, Observable, Subscription } from 'rxjs';

test('Must unsubscribe everywhere', () => {
  let observable1$ = new Observable<string>();
  let subscription1 = new Subscription();
  const observable1Spy = jest
    .spyOn(observable1$, 'subscribe')
    .mockReturnValue(subscription1);
  const unsubscription1Spy = jest.spyOn(subscription1, 'unsubscribe');

  let observable2$ = new Observable<string>();
  let subscription2 = new Subscription();
  const observable2Spy = jest
    .spyOn(observable2$, 'subscribe')
    .mockReturnValue(subscription2);
  const unsubscription2Spy = jest.spyOn(subscription1, 'unsubscribe');

  expect(observable1Spy).toHaveBeenCalledTimes(0);
  expect(unsubscription1Spy).toHaveBeenCalledTimes(0);
  expect(subscription1.closed).toBeFalsy();
  expect(observable2Spy).toHaveBeenCalledTimes(0);
  expect(unsubscription2Spy).toHaveBeenCalledTimes(0);
  expect(subscription2.closed).toBeFalsy();

  let combined$ = combineLatest([observable1$, observable2$]);
  const subscription3 = combined$.subscribe((value) => {});

  expect(subscription3.closed).toBeFalsy();
  expect(observable1Spy).toHaveBeenCalledTimes(1);
  expect(unsubscription1Spy).toHaveBeenCalledTimes(0);
  expect(subscription1.closed).toBeFalsy();
  expect(observable2Spy).toHaveBeenCalledTimes(1);
  expect(unsubscription2Spy).toHaveBeenCalledTimes(0);
  expect(subscription2.closed).toBeFalsy();

  subscription3.unsubscribe();

  expect(subscription3.closed).toBeTruthy();
  expect(observable1Spy).toHaveBeenCalledTimes(1);
  expect(unsubscription1Spy).toHaveBeenCalledTimes(1);
  expect(subscription1.closed).toBeTruthy();
  expect(observable2Spy).toHaveBeenCalledTimes(1);
  expect(unsubscription2Spy).toHaveBeenCalledTimes(1);
  expect(subscription2.closed).toBeTruthy();
});

Reproduction URL

https://stackblitz.com/edit/rxjs-issue-7318?file=test%2Funsubscribe.spec.ts&terminal=test

Version

7.x.x

Environment

No response

Additional context

Change RXJS to version "^6.6.7" in package.json, and the test will pass

We can see in the code that subscription to an underlying observable is not kept in memory anymore:

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions