From 51462f369b376615bc9d761dcaa5d822ea1ff8ee Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tom=C3=A1s=20Girardi?= Date: Tue, 26 Aug 2014 12:01:49 -0400 Subject: [PATCH 1/2] Allow clearInterval to clear it's own interval As described in issue #655, the handler of an interval cannot successfully clear the same interval that generated it's invocation. Solve this issue by changing the order in which interval's handlers are called and then rescheduled to: first reschedule it and then call it. The actual order (call first then reschedule) produces that, during the execution of the interval's handler, the handler is not registered as a function to run after a timeout or interval ("scheduledFunctions"), because it was previously unregistered. Consequently, if the handler calls clearInterval, that function wont be able to find the handler and remove it completely. --- src/core/DelayedFunctionScheduler.js | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/core/DelayedFunctionScheduler.js b/src/core/DelayedFunctionScheduler.js index 8079c70e..adb93d1e 100644 --- a/src/core/DelayedFunctionScheduler.js +++ b/src/core/DelayedFunctionScheduler.js @@ -127,11 +127,12 @@ getJasmineRequireObj().DelayedFunctionScheduler = function() { for (var i = 0; i < funcsToRun.length; ++i) { var funcToRun = funcsToRun[i]; - funcToRun.funcToCall.apply(null, funcToRun.params || []); if (funcToRun.recurring) { reschedule(funcToRun); } + + funcToRun.funcToCall.apply(null, funcToRun.params || []); } } while (scheduledLookup.length > 0 && // checking first if we're out of time prevents setTimeout(0) From eb48c836491b504963b7e01107c7843d19f4af3c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tom=C3=A1s=20Girardi?= Date: Thu, 28 Aug 2014 20:58:30 -0400 Subject: [PATCH 2/2] Add specs for intervals that "clear themselves" Add specs to test if issue #655 is present: the handler of an interval cannot successfully clear the same interval that generated it's invocation. The most direct test consist in setting an interval with a handler that calls clearInterval over that same interval and make the clock tick for double of it's period. If the issue is present the interval's handler will be called twice. If the issue is not present, the first invocation of the handler will avoid a second one (because of the clearInterval). Another test is included in order to check if recurring scheduled functions are rescheduled before being called. Doing this in the reverse order is the exact cause of the issue. --- spec/core/ClockSpec.js | 18 ++++++++++++++++++ spec/core/DelayedFunctionSchedulerSpec.js | 13 +++++++++++++ 2 files changed, 31 insertions(+) diff --git a/spec/core/ClockSpec.js b/spec/core/ClockSpec.js index e7f92811..18d281bf 100644 --- a/spec/core/ClockSpec.js +++ b/spec/core/ClockSpec.js @@ -323,6 +323,24 @@ describe("Clock (acceptance)", function() { expect(clearedFn).not.toHaveBeenCalled(); }); + it("can clear a previously set interval using that interval's handler", function() { + var spy = jasmine.createSpy('spy'), + delayedFunctionScheduler = new j$.DelayedFunctionScheduler(), + mockDate = { install: function() {}, tick: function() {}, uninstall: function() {} }, + clock = new j$.Clock({setInterval: function() {}}, delayedFunctionScheduler, mockDate), + intervalId; + + clock.install(); + + intervalId = clock.setInterval(function() { + spy(); + clock.clearInterval(intervalId); + }, 100); + clock.tick(200); + + expect(spy.calls.count()).toEqual(1); + }); + it("correctly schedules functions after the Clock has advanced", function() { var delayedFn1 = jasmine.createSpy('delayedFn1'), delayedFunctionScheduler = new j$.DelayedFunctionScheduler(), diff --git a/spec/core/DelayedFunctionSchedulerSpec.js b/spec/core/DelayedFunctionSchedulerSpec.js index 7048b9e8..37dfa766 100644 --- a/spec/core/DelayedFunctionSchedulerSpec.js +++ b/spec/core/DelayedFunctionSchedulerSpec.js @@ -242,5 +242,18 @@ describe("DelayedFunctionScheduler", function() { expect(innerFn).toHaveBeenCalled(); }); + it("executes recurring functions after rescheduling them", function () { + var scheduler = new j$.DelayedFunctionScheduler(), + recurring = function() { + expect(scheduler.scheduleFunction).toHaveBeenCalled(); + }; + + scheduler.scheduleFunction(recurring, 10, [], true); + + spyOn(scheduler, "scheduleFunction"); + + scheduler.tick(10); + }); + });