Skip to content

Commit 7d722d4

Browse files
cartantbenlesh
authored andcommitted
fix(scheduler): prevent unwanted clearInterval (#3044)
* test(scheduler): add interval recycling tests * fix(scheduler): prevent unwanted clearInterval In AsyncAction, this.pending was assigned before the call to recycleAsyncId. That prevented the interval from being re-used resulting in the interval being cleared and re-created for each notification. Closes #3042
1 parent d77e3d7 commit 7d722d4

File tree

2 files changed

+57
-4
lines changed

2 files changed

+57
-4
lines changed

spec/schedulers/AsapScheduler-spec.ts

Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,59 @@ describe('Scheduler.asap', () => {
4040
sandbox.restore();
4141
});
4242

43+
it('should reuse the interval for recursively scheduled actions with the same delay', () => {
44+
const sandbox = sinon.sandbox.create();
45+
const fakeTimer = sandbox.useFakeTimers();
46+
// callThrough is missing from the declarations installed by the typings tool in stable
47+
const stubSetInterval = (<any> sinon.stub(global, 'setInterval')).callThrough();
48+
function dispatch(state: any): void {
49+
state.index += 1;
50+
if (state.index < 3) {
51+
(<any> this).schedule(state, state.period);
52+
}
53+
}
54+
const period = 50;
55+
const state = { index: 0, period };
56+
asap.schedule(dispatch, period, state);
57+
expect(state).to.have.property('index', 0);
58+
expect(stubSetInterval).to.have.property('callCount', 1);
59+
fakeTimer.tick(period);
60+
expect(state).to.have.property('index', 1);
61+
expect(stubSetInterval).to.have.property('callCount', 1);
62+
fakeTimer.tick(period);
63+
expect(state).to.have.property('index', 2);
64+
expect(stubSetInterval).to.have.property('callCount', 1);
65+
stubSetInterval.restore();
66+
sandbox.restore();
67+
});
68+
69+
it('should not reuse the interval for recursively scheduled actions with a different delay', () => {
70+
const sandbox = sinon.sandbox.create();
71+
const fakeTimer = sandbox.useFakeTimers();
72+
// callThrough is missing from the declarations installed by the typings tool in stable
73+
const stubSetInterval = (<any> sinon.stub(global, 'setInterval')).callThrough();
74+
function dispatch(state: any): void {
75+
state.index += 1;
76+
state.period -= 1;
77+
if (state.index < 3) {
78+
(<any> this).schedule(state, state.period);
79+
}
80+
}
81+
const period = 50;
82+
const state = { index: 0, period };
83+
asap.schedule(dispatch, period, state);
84+
expect(state).to.have.property('index', 0);
85+
expect(stubSetInterval).to.have.property('callCount', 1);
86+
fakeTimer.tick(period);
87+
expect(state).to.have.property('index', 1);
88+
expect(stubSetInterval).to.have.property('callCount', 2);
89+
fakeTimer.tick(period);
90+
expect(state).to.have.property('index', 2);
91+
expect(stubSetInterval).to.have.property('callCount', 3);
92+
stubSetInterval.restore();
93+
sandbox.restore();
94+
});
95+
4396
it('should schedule an action to happen later', (done: MochaDone) => {
4497
let actionHappened = false;
4598
asap.schedule(() => {

src/scheduler/AsyncAction.ts

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -29,10 +29,6 @@ export class AsyncAction<T> extends Action<T> {
2929
// Always replace the current state with the new state.
3030
this.state = state;
3131

32-
// Set the pending flag indicating that this action has been scheduled, or
33-
// has recursively rescheduled itself.
34-
this.pending = true;
35-
3632
const id = this.id;
3733
const scheduler = this.scheduler;
3834

@@ -61,6 +57,10 @@ export class AsyncAction<T> extends Action<T> {
6157
this.id = this.recycleAsyncId(scheduler, id, delay);
6258
}
6359

60+
// Set the pending flag indicating that this action has been scheduled, or
61+
// has recursively rescheduled itself.
62+
this.pending = true;
63+
6464
this.delay = delay;
6565
// If this action has already an async Id, don't request a new one.
6666
this.id = this.id || this.requestAsyncId(scheduler, this.id, delay);

0 commit comments

Comments
 (0)