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

throttle operator throws error RangeError: Maximum call stack size exceeded #6034

Closed
OliverJAsh opened this issue Feb 19, 2021 · 10 comments
Closed

Comments

@OliverJAsh
Copy link
Contributor

OliverJAsh commented Feb 19, 2021

RxJS version:
6.6.3 (latest at time of writing)

Code to reproduce:

https://stackblitz.com/edit/rxjs-compat-ywrxqr

import * as Rx from "rxjs";
import { throttle } from "rxjs/operators";

const source = Rx.of(1).pipe(
  throttle(() => Rx.EMPTY, { leading: false, trailing: true })
);

source.subscribe(x => console.log(x));

Expected behavior:
It logs 1 once.

Actual behavior:
It logs 1 many times and then errors with RangeError: Maximum call stack size exceeded.

image

Additional information:

The reduced test case is a bit contrived. In practice my duration selector has a bit more logic:

throttle(n => n === 10 ? Rx.timer(100) : Rx.EMPTY, { leading: false, trailing: true })
@OliverJAsh
Copy link
Contributor Author

I was hoping I could workaround it like so:

-throttle(() => Rx.EMPTY, { leading: false, trailing: true })
+throttle(() => Rx.scheduled(Rx.EMPTY, Rx.async), { leading: false, trailing: true })

… so it completes asynchronously rather than synchronously. However IIUC this doesn't work because of another bug with throttle: #5732.

@OliverJAsh
Copy link
Contributor Author

Note this issue only occurs when trailing: true. Example that uses trailing: false: https://stackblitz.com/edit/rxjs-compat-mygu4y.

@OliverJAsh
Copy link
Contributor Author

OliverJAsh commented Feb 19, 2021

In RxJS v7 beta, the behaviour is different but still incorrect (at least in my mind): https://stackblitz.com/edit/rxjs-compat-mmjf6c. This never logs anything. Update: never mind, see #6034 (comment).

@josepot
Copy link
Contributor

josepot commented Feb 19, 2021

In RxJS v7 beta, the behaviour is different but still incorrect (at least in my mind): https://stackblitz.com/edit/rxjs-compat-mmjf6c. This never logs anything.

That's the behavior that I would expect: given the fact that leading is set to false and that the source is a sync observable that emits once and completes in the same tick.

What's the behavior that you were expecting?

@OliverJAsh
Copy link
Contributor Author

My understanding was that if trailing is set to true, the throttled Observable should always emit the last value that the source emitted before completion.

#5732

@josepot
Copy link
Contributor

josepot commented Feb 19, 2021

My understanding was that if trailing is set to true, the throttled Observable should always emit the last value that the source emitted before completion.

#5732

I could be completely wrong, of course, but what I would expect from trailing set to true is for the resulting observable to emit the latest value that the source emitted while the durationSelector observable was "active". If the source emits and completes synchronously, then I would expect for the operator to not capture any values during the durationSelector observable... Because well, there is no point to subscribing to the durationSelector, since the source has completed already. Again, I could be completely wrong, of course. I'm just saying how I understand this operator.

@OliverJAsh
Copy link
Contributor Author

OliverJAsh commented Feb 21, 2021

Another weird discovery: in v7.0.0-beta.10 this doesn't throw an error but it does still log 1 over 1000 times.

import * as Rx from "rxjs";
import { throttle } from "rxjs/operators";

const source = Rx.of(1).pipe(
  throttle(() => Rx.of(1), { leading: false, trailing: true })
);

source.subscribe(x => console.log(x));

https://stackblitz.com/edit/rxjs-compat-vswz6n

If someone can confirm this is not expected behaviour, I can try to send a PR with a test and a fix.

If I understand correctly, this is the expected behaviour as @josepot says:

If the source emits and completes synchronously, then I would expect for the operator to not capture any values during the durationSelector observable

So source should emit nothing and complete synchronously?


In RxJS v7 beta, the behaviour is different but still incorrect (at least in my mind): https://stackblitz.com/edit/rxjs-compat-mmjf6c. This never logs anything.

Ah, I believe this is because of this change: 4af0227. In my example the duration selector never emits anything, it only completes. That makes sense.

@benlesh
Copy link
Member

benlesh commented Feb 26, 2021

The original bug is fixed in 7.0.0.

However, the bug reported here is still outstanding.

It begs the question "why would anyone do this?" but it still needs to be fixed.

@benlesh
Copy link
Member

benlesh commented Feb 26, 2021

Closing this, as it's fixed in 7. I'm uncertain that this needs fixed in 6, as it's sort of an edge case, but I'm happy to reopen it and take anyone's PR on the matter if they want to put in the time.

@benlesh
Copy link
Member

benlesh commented Feb 26, 2021

Other issue is here: #6058

@benlesh benlesh closed this as completed Feb 26, 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