ReactiveX / ReactiveX/rxjs

AsapScheduler can interrupt random intervals

Open
#7,593 1 comment 0 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

Describe the bug

An interval scheduled somewhere else (including rxjs timer) can be unduly canceled by AsapScheduler implementation:

  1. Create a timer with timer(0, intervalDuration, asapScheduler), with intervalDuration > 0
  2. The timer method calls scheduler.schedule(callback, 0) (https://github.com/ReactiveX/rxjs/blob/7.8.2/src/internal/observable/timer.ts#L170C12-L170C30)
  3. The asapScheduler generates an ID from Immediate, we can call it immediateId (https://github.com/ReactiveX/rxjs/blob/7.8.2/src/internal/util/Immediate.ts#L24)
  4. The callback reschedules itself calling schedule(undefined, intervalDuration)
  5. In https://github.com/ReactiveX/rxjs/blob/7.8.2/src/internal/scheduler/AsyncAction.ts#L52, this.id != null, so this.recycleAsyncId(scheduler, id, delay) is called with the id = immediateId and non-zero delay.
  6. asapAction falls back to ayncAction implementation of recycleAsyncId because delay is non-zero (https://github.com/ReactiveX/rxjs/blob/7.8.2/src/internal/scheduler/AsapAction.ts#L30)
  7. asynAction.recycleAsyncId unduly clears an interval with id = immediateId https://github.com/ReactiveX/rxjs/blob/7.8.2/src/internal/scheduler/AsyncAction.ts#L79
Expected behavior

The AsapScheduler should be protected in order not to affect unrelated code.

The AsyncScheduler could be protected as well by using the type-system to ensure that only ids generated by setInterval are passed to clearInterval

Reproduction code
import { asapScheduler, timer } from 'rxjs';

let n = 0;

const a = setInterval(() => console.log(n++), 200);

console.log(`interval id: ${a}`);
setTimeout(() => {
  timer(0, 10000, asapScheduler).subscribe(() => console.log("I did not eat the other interval"));
}, 1);

setTimeout(() => {
  timer(0, 10000, asapScheduler).subscribe(() => console.log("I did not eat the other interval"));
}, 2);

setTimeout(() => {
  timer(0, 10000, asapScheduler).subscribe(() => console.log("I ate the other interval"));
}, 3);
Reproduction URL

No response

Version

<= 7.8.2

Environment

Any environment. Happens at least in

  • Firefox 147
  • Chromium 145
  • Node version v22.20.0

The reproduction code was tested in Node 22.20.0

Additional context

No response

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

Trace the scheduling flow through src/internal/scheduler/AsapAction.ts and AsyncAction.ts, using Immediate.ts and timer.ts as the documented entry points. Run the reproduction with the listed browser or Node environments, then verify that an asapScheduler timer no longer cancels an unrelated setInterval and add regression coverage for that behavior.

Written by the indexing model from the issue text.

Assessment

Tech stack
javascript, typescript
Domain
tooling
Issue type
Bug
Difficulty
3/5
Estimated time
1-2 days
Activity status
Stale
Clarity
Clearly specified
Newbie friendliness
56/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.