diff --git a/spec/schedulers/AnimationFrameScheduler-spec.ts b/spec/schedulers/AnimationFrameScheduler-spec.ts index eae794b1cb3..567dc6b0c89 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,25 @@ 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(); + reschedule = false; + } else { + expect(result).to.deep.equal(['work', 'task 1', 'task 2', 'work']); + sandbox.restore(); + done(); + } + } + animationFrameScheduler.schedule(work); + }); }); diff --git a/spec/schedulers/AsapScheduler-spec.ts b/spec/schedulers/AsapScheduler-spec.ts index 54b55349eb0..e2e5d3a09e9 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,25 @@ 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(); + reschedule = false; + } else { + expect(result).to.deep.equal(['work', 'task 1', 'task 2', 'work']); + sandbox.restore(); + done(); + } + } + asapScheduler.schedule(work); + }); }); diff --git a/src/internal/scheduler/AnimationFrameAction.ts b/src/internal/scheduler/AnimationFrameAction.ts index 771212f73d9..cfe13a54d85 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 f8f5116e501..3428f03107c 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; }