Skip to content

scan() operator unexpectedly resets or fails to accumulate emissions across multiple emissions #7540

Open
@phillipwildhirt

Description

@phillipwildhirt

Describe the bug

When using the scan() operator in RxJS (version 7.8.1), the accumulator parameter (a) does not correctly retain previous values across emissions. Instead, it appears to be empty or reset with each new emission, even though the index parameter (i) increments as expected. This suggests that scan() is receiving consecutive emissions but failing to persist the accumulated state.

Expected behavior

  • The scan() operator should correctly accumulate all previous emissions.
  • The accumulator parameter (a) should contain the history of all prior emissions, growing over time.
Index: 1  
Accumulator: []  
Index: 2  
Accumulator: [{ time: 1711010000000, view: [expandedTabs] }]  
Index: 3  
Accumulator: [{ time: 1711010000000, view: [expandedTabs] }, { time: 1711010500000, view: [] }]

Actual Behavior:

  • The accumulator appears empty ([]) on each emission, despite the i index increasing.
  • It behaves as though scan() is reinitializing or not retaining prior emissions properly.
Index: 1  
Accumulator: []  
Index: 2  
Accumulator: []  
Index: 3  
Accumulator: []

Reproduction code

import { ReplaySubject, scan, map, tap, Observable } from 'rxjs';

enum DashboardViews {
  expandedTabs = 'expandedTabs'
}

const views$ = new ReplaySubject<DashboardViews[]>(1);

const viewChange$: Observable<boolean> = views$.pipe(
  map(view => ({ view, time: new Date().valueOf() })),
  scan((a, c, i) => {
    console.log('Index:', i);           // Index increases correctly
    console.log('Accumulator:', a);     // Accumulator is unexpectedly empty on each emission
    a.push(c);
    return a;
  }, [] as { time: number, view: DashboardViews[] }[]),
  tap(v => console.log('Accumulated values:', v)),
  map((views) => {
    if (views.length < 2) return false;
    const curr = views[views.length - 1];
    const prev = views[views.length - 2];
    return prev.view.includes(DashboardViews.expandedTabs) !== curr.view.includes(DashboardViews.expandedTabs);
  })
);

// Simulate emissions
views$.next([DashboardViews.expandedTabs]);
views$.next([]);
views$.next([DashboardViews.expandedTabs]);

viewChange$.subscribe(v => console.log('viewChange', v));

Reproduction URL

No response

Version

7.8.1

Environment

  • RxJS version: 7.81
  • Angular version: 18.2.1
  • Node.js version: 20.18.1
  • Browser: Chrome 133
  • OS: macOS 14.7.4 (23H420)

Additional context

  • The issue persists even when using shareReplay(), defer(), and ReplaySubject() with a larger buffer size, suggesting that this may be a bug in the scan() operator itself.
  • The i index increasing indicates that the emissions are consecutive, yet the accumulator does not persist previous values.
  • This behavior does not align with the expected behavior of scan() in RxJS.

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