From ad36f57c659ef7b67fcfecf777aeafb5fa774829 Mon Sep 17 00:00:00 2001 From: Klaus Meinhardt Date: Mon, 14 Mar 2022 14:31:48 +0100 Subject: [PATCH] fix: animationFrameScheduler and asapScheduler no longer executing actions Fixes #6854 --- .../AnimationFrameScheduler-spec.ts | 25 ++++++++++++++++++- spec/schedulers/AsapScheduler-spec.ts | 25 ++++++++++++++++++- .../scheduler/AnimationFrameAction.ts | 4 +-- src/internal/scheduler/AsapAction.ts | 4 +-- 4 files changed, 52 insertions(+), 6 deletions(-) diff --git a/spec/schedulers/AnimationFrameScheduler-spec.ts b/spec/schedulers/AnimationFrameScheduler-spec.ts index eae794b1cb..b7a3221516 100644 --- a/spec/schedulers/AnimationFrameScheduler-spec.ts +++ b/spec/schedulers/AnimationFrameScheduler-spec.ts @@ -1,6 +1,6 @@ import { expect } from 'chai'; import * as sinon from 'sinon'; -import { animationFrameScheduler, Subscription, merge } from 'rxjs'; +import {animationFrameScheduler, Subscription, merge, SchedulerAction} from 'rxjs'; import { delay } from 'rxjs/operators'; import { TestScheduler } from 'rxjs/testing'; import { observableMatcher } from '../helpers/observableMatcher'; @@ -238,4 +238,27 @@ describe('Scheduler.animationFrame', () => { done(); }); }); + + it('should handle actions scheduled during flush before current action is rescheduled', (done) => { + const sandbox = sinon.createSandbox(); + + const result: string[] = []; + let reschedule = true; + function work(this: SchedulerAction) { + result.push('work'); + if (reschedule) { + animationFrameScheduler.schedule(() => result.push('task 1')); + animationFrameScheduler.schedule(() => result.push('task 2')); + this.schedule(); + expect(result).to.deep.equal(['work']); + reschedule = false; + } else { + expect(result).to.deep.equal(['work', 'task 1', 'task 2', 'work']); + sandbox.restore(); + done(); + } + } + animationFrameScheduler.schedule(work); + expect(result).to.deep.equal([]); + }); }); diff --git a/spec/schedulers/AsapScheduler-spec.ts b/spec/schedulers/AsapScheduler-spec.ts index 54b55349eb..d5a1658004 100644 --- a/spec/schedulers/AsapScheduler-spec.ts +++ b/spec/schedulers/AsapScheduler-spec.ts @@ -1,6 +1,6 @@ import { expect } from 'chai'; import * as sinon from 'sinon'; -import { asapScheduler, Subscription, SchedulerAction, merge } from 'rxjs'; +import {asapScheduler, Subscription, SchedulerAction, merge, animationFrameScheduler} from 'rxjs'; import { delay } from 'rxjs/operators'; import { TestScheduler } from 'rxjs/testing'; import { observableMatcher } from '../helpers/observableMatcher'; @@ -288,4 +288,27 @@ describe('Scheduler.asap', () => { done(); }); }); + + it('should handle actions scheduled during flush before current action is rescheduled', (done) => { + const sandbox = sinon.createSandbox(); + + const result: string[] = []; + let reschedule = true; + function work(this: SchedulerAction) { + result.push('work'); + if (reschedule) { + asapScheduler.schedule(() => result.push('task 1')); + asapScheduler.schedule(() => result.push('task 2')); + this.schedule(); + expect(result).to.deep.equal(['work']); + reschedule = false; + } else { + expect(result).to.deep.equal(['work', 'task 1', 'task 2', 'work']); + sandbox.restore(); + done(); + } + } + asapScheduler.schedule(work); + expect(result).to.deep.equal([]); + }); }); diff --git a/src/internal/scheduler/AnimationFrameAction.ts b/src/internal/scheduler/AnimationFrameAction.ts index 771212f73d..cfe13a54d8 100644 --- a/src/internal/scheduler/AnimationFrameAction.ts +++ b/src/internal/scheduler/AnimationFrameAction.ts @@ -27,10 +27,10 @@ export class AnimationFrameAction extends AsyncAction { if ((delay != null && delay > 0) || (delay == null && this.delay > 0)) { return super.recycleAsyncId(scheduler, id, delay); } - // If the scheduler queue has no remaining actions with the same async id, + // If the scheduler queue has no remaining actions for the next schedule, // cancel the requested animation frame and set the scheduled flag to // undefined so the next AnimationFrameAction will request its own. - if (!scheduler.actions.some((action) => action.id === id)) { + if (scheduler._scheduled === id && !scheduler.actions.some((action) => action.id === id)) { animationFrameProvider.cancelAnimationFrame(id); scheduler._scheduled = undefined; } diff --git a/src/internal/scheduler/AsapAction.ts b/src/internal/scheduler/AsapAction.ts index f8f5116e50..3428f03107 100644 --- a/src/internal/scheduler/AsapAction.ts +++ b/src/internal/scheduler/AsapAction.ts @@ -27,10 +27,10 @@ export class AsapAction extends AsyncAction { if ((delay != null && delay > 0) || (delay == null && this.delay > 0)) { return super.recycleAsyncId(scheduler, id, delay); } - // If the scheduler queue has no remaining actions with the same async id, + // If the scheduler queue has no remaining actions for the next schedule, // cancel the requested microtask and set the scheduled flag to undefined // so the next AsapAction will request its own. - if (!scheduler.actions.some((action) => action.id === id)) { + if (scheduler._scheduled === id && !scheduler.actions.some((action) => action.id === id)) { immediateProvider.clearImmediate(id); scheduler._scheduled = undefined; }