RxJS expand won't complete after a certain amount of calls. Does it have a limit?

Viewed 43

I'm trying to count down from a number, lets say 8000, then I reduce all the values into an array. If I count down by 100, 50 or even 10, expand completes and I can see the array in the console, but if I count down by 1 (step = 1), it just stops at a certain number and does nothing, won't continue, won't complete. There are no errors in the console.

Anyone knows what's going on? Does expand have a limit of recursive calls?

const fromValue = 8000;
const step = 1;   // Change this value to 1, 10, 100...

rxjs.of(fromValue).pipe(
  rxjs.expand(value => {
    console.log(value);
    return value > 0 ? rxjs.of(value - step) : rxjs.EMPTY;
  }),
  rxjs.reduce((acc, value) => [...acc, value], [])
).subscribe(result => console.log(result));
<script  src="https://unpkg.com/rxjs@7.5.6/dist/bundles/rxjs.umd.min.js"></script>

2 Answers

I think this could be because you reach the max stack trace size but I'm not 100% sure about it because it doesn't throw any error.

One way to fix this could be using observeOn(queueScheduler) so the next notifications will be emitted one after another and won't create more and more nested calls.

import { of, EMPTY, expand, reduce, queueScheduler, observeOn } from 'rxjs';

const fromValue = 8000;
const step = 1;

of(fromValue).pipe(
  expand(value => {
    console.log(value);
    return value > 0 ? of(value - step).pipe(observeOn(queueScheduler)) : EMPTY;
  }),
  reduce((acc, value) => [...acc, value], [])
)
.subscribe(result => console.log(result));

Live demo: https://stackblitz.com/edit/rxjs-wnuulh?devtoolsheight=60&file=index.ts

JavaScript is running out of Heap size memory, maybe find a more efficient code to perform this logic?

enter image description here

Note: dont use stack overflow console, use the console of the browser!

const fromValue = 8000;
const step = 1; // Change this value to 1, 10, 100...
console.clear();
rxjs.of(fromValue).pipe(
  rxjs.expand(value => {
    console.log(value, window.performance.memory);
    return value > 0 ? rxjs.of(value - step) : rxjs.EMPTY;
  }),
  rxjs.tap(() => console.log('before array', window.performance.memory)),
  rxjs.toArray(),
  rxjs.tap(() => console.log('after array', window.performance.memory)),
).subscribe(result => {
  console.log('final array', window.performance.memory);
  console.log(result);
});
<script src="https://unpkg.com/rxjs@7.5.6/dist/bundles/rxjs.umd.min.js"></script>


Alternate way

let fromValue = 8000;
const step = 1; // Change this value to 1, 10, 100...
const output = [];
console.clear();
console.log('start')
rxjs.interval().pipe(
  rxjs.tap(() => {
console.log('iteration', fromValue)
    output.push(fromValue);
    fromValue = fromValue - step;
  }),
  rxjs.takeWhile((x) => fromValue >= 0),
  rxjs.last(),
).subscribe(() => {
  console.log(output);
});
<script src="https://unpkg.com/rxjs@7.5.6/dist/bundles/rxjs.umd.min.js"></script>

Related