Describe the bug
An interval scheduled somewhere else (including rxjs timer) can be unduly canceled by AsapScheduler implementation:
- Create a timer with
timer(0, intervalDuration, asapScheduler), with intervalDuration > 0
- The timer method calls
scheduler.schedule(callback, 0) (https://github.com/ReactiveX/rxjs/blob/7.8.2/src/internal/observable/timer.ts#L170C12-L170C30)
- 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)
- The callback reschedules itself calling
schedule(undefined, intervalDuration)
- 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.
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)
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
Describe the bug
An interval scheduled somewhere else (including rxjs
timer) can be unduly canceled byAsapSchedulerimplementation:timer(0, intervalDuration, asapScheduler), withintervalDuration > 0scheduler.schedule(callback, 0)(https://github.com/ReactiveX/rxjs/blob/7.8.2/src/internal/observable/timer.ts#L170C12-L170C30)asapSchedulergenerates an ID fromImmediate, we can call itimmediateId(https://github.com/ReactiveX/rxjs/blob/7.8.2/src/internal/util/Immediate.ts#L24)schedule(undefined, intervalDuration)this.id != null, sothis.recycleAsyncId(scheduler, id, delay)is called with theid = immediateIdand non-zerodelay.asapActionfalls back toayncActionimplementation ofrecycleAsyncIdbecausedelayis non-zero (https://github.com/ReactiveX/rxjs/blob/7.8.2/src/internal/scheduler/AsapAction.ts#L30)asynAction.recycleAsyncIdunduly clears an interval withid = immediateIdhttps://github.com/ReactiveX/rxjs/blob/7.8.2/src/internal/scheduler/AsyncAction.ts#L79Expected behavior
The
AsapSchedulershould be protected in order not to affect unrelated code.The
AsyncSchedulercould be protected as well by using the type-system to ensure that onlyids generated bysetIntervalare passed toclearIntervalReproduction code
Reproduction URL
No response
Version
<= 7.8.2
Environment
Any environment. Happens at least in
The reproduction code was tested in Node 22.20.0
Additional context
No response