Skip to content

Commit 8bbfa4e

Browse files
authored
fix(animationFrameScheduler): some tasks are never flushed and sometimes it breaks completely (#7444)
* fix(tests): Minor fix sandbox fake timers Sandbox was not cleaning up correctly and making fail the test that I added. * fix(animationFrameSchduler): Add failing tests * fix(animationFrameScheduler): Fix the scenarios in added tests * chore(animationFrameScheduler): minor code improvement * chore(animationFrameScheduler): minor test code improvement Make test assert order more clear
1 parent 4a2d0d2 commit 8bbfa4e

File tree

4 files changed

+62
-5
lines changed

4 files changed

+62
-5
lines changed

spec/schedulers/AnimationFrameScheduler-spec.ts

+46
Original file line numberDiff line numberDiff line change
@@ -141,6 +141,52 @@ describe('Scheduler.animationFrame', () => {
141141
}
142142
});
143143

144+
it('should schedule next frame actions from a delayed one', (done) => {
145+
animationFrame.schedule(() => {
146+
animationFrame.schedule(() => { done(); });
147+
}, 1);
148+
});
149+
150+
it('should schedule 2 actions for a subsequent frame', (done) => {
151+
let runFirst = false;
152+
animationFrame.schedule(() => {
153+
animationFrame.schedule(() => { runFirst = true; });
154+
animationFrame.schedule(() => {
155+
if (runFirst) {
156+
done();
157+
} else {
158+
done(new Error('First action did not run'));
159+
}
160+
});
161+
});
162+
});
163+
164+
it('should handle delayed action without affecting next frame actions', (done) => {
165+
let runDelayed = false;
166+
let runFirst = false;
167+
animationFrame.schedule(() => {
168+
animationFrame.schedule(() => {
169+
if (!runDelayed) {
170+
done(new Error('Delayed action did not run'));
171+
return;
172+
}
173+
runFirst = true;
174+
});
175+
animationFrame.schedule(() => {
176+
if (!runFirst) {
177+
done(new Error('First action did not run'));
178+
} else {
179+
done();
180+
}
181+
});
182+
183+
// This action will execute before the next frame because the delay is less than the one of the frame
184+
animationFrame.schedule(() => {
185+
runDelayed = true;
186+
}, 1);
187+
});
188+
});
189+
144190
it('should not execute rescheduled actions when flushing', (done) => {
145191
let flushCount = 0;
146192
let scheduledIndices: number[] = [];

spec/schedulers/AsapScheduler-spec.ts

+8-2
Original file line numberDiff line numberDiff line change
@@ -66,7 +66,7 @@ describe('Scheduler.asap', () => {
6666
const sandbox = sinon.createSandbox();
6767
const fakeTimer = sandbox.useFakeTimers();
6868
// callThrough is missing from the declarations installed by the typings tool in stable
69-
const stubSetInterval = (<any> sandbox.stub(global, 'setInterval')).callThrough();
69+
const stubSetInterval = (<any> sandbox.stub(fakeTimer, 'setInterval')).callThrough();
7070
const period = 50;
7171
const state = { index: 0, period };
7272
type State = typeof state;
@@ -92,7 +92,7 @@ describe('Scheduler.asap', () => {
9292
const sandbox = sinon.createSandbox();
9393
const fakeTimer = sandbox.useFakeTimers();
9494
// callThrough is missing from the declarations installed by the typings tool in stable
95-
const stubSetInterval = (<any> sandbox.stub(global, 'setInterval')).callThrough();
95+
const stubSetInterval = (<any> sandbox.stub(fakeTimer, 'setInterval')).callThrough();
9696
const period = 50;
9797
const state = { index: 0, period };
9898
type State = typeof state;
@@ -148,6 +148,12 @@ describe('Scheduler.asap', () => {
148148
}, 0, 0);
149149
});
150150

151+
it('should schedule asap actions from a delayed one', (done) => {
152+
asap.schedule(() => {
153+
asap.schedule(() => { done(); });
154+
}, 1);
155+
});
156+
151157
it('should cancel the setImmediate if all scheduled actions unsubscribe before it executes', (done) => {
152158
let asapExec1 = false;
153159
let asapExec2 = false;

src/internal/scheduler/AnimationFrameAction.ts

+1-1
Original file line numberDiff line numberDiff line change
@@ -33,7 +33,7 @@ export class AnimationFrameAction<T> extends AsyncAction<T> {
3333
// cancel the requested animation frame and set the scheduled flag to
3434
// undefined so the next AnimationFrameAction will request its own.
3535
const { actions } = scheduler;
36-
if (id != null && actions[actions.length - 1]?.id !== id) {
36+
if (id != null && id === scheduler._scheduled && actions[actions.length - 1]?.id !== id) {
3737
animationFrameProvider.cancelAnimationFrame(id as number);
3838
scheduler._scheduled = undefined;
3939
}

src/internal/scheduler/AnimationFrameScheduler.ts

+7-2
Original file line numberDiff line numberDiff line change
@@ -13,8 +13,13 @@ export class AnimationFrameScheduler extends AsyncScheduler {
1313
// are removed from the actions array and that can shift actions that are
1414
// scheduled to be executed in a subsequent flush into positions at which
1515
// they are executed within the current flush.
16-
const flushId = this._scheduled;
17-
this._scheduled = undefined;
16+
let flushId;
17+
if (action) {
18+
flushId = action.id;
19+
} else {
20+
flushId = this._scheduled;
21+
this._scheduled = undefined;
22+
}
1823

1924
const { actions } = this;
2025
let error: any;

0 commit comments

Comments
 (0)