diff --git a/spec/core/ClockSpec.js b/spec/core/ClockSpec.js index 350dc85e..37c1bc33 100644 --- a/spec/core/ClockSpec.js +++ b/spec/core/ClockSpec.js @@ -680,7 +680,7 @@ describe("Clock (acceptance)", function() { expect(actualTimes).toEqual([baseTime.getTime(), baseTime.getTime() + 1, baseTime.getTime() + 3]); }) - it('should be able to clear a timeout', function () { + it('correctly clears a scheduled timeout while the Clock is advancing', function () { var delayedFunctionScheduler = new jasmineUnderTest.DelayedFunctionScheduler(), global = {Date: Date, setTimeout: undefined}, mockDate = new jasmineUnderTest.MockDate(global), @@ -694,10 +694,26 @@ describe("Clock (acceptance)", function() { global.clearTimeout(timerId2); }, 100); - timerId2 = global.setTimeout(() => { - fail(); - }, 100); + timerId2 = global.setTimeout(fail, 100); clock.tick(100); }); + + it('correctly clears a scheduled interval while the Clock is advancing', function () { + var delayedFunctionScheduler = new jasmineUnderTest.DelayedFunctionScheduler(), + global = {Date: Date, setTimeout: undefined}, + mockDate = new jasmineUnderTest.MockDate(global), + clock = new jasmineUnderTest.Clock(global, function () { return delayedFunctionScheduler; }, mockDate); + + clock.install(); + + var timerId2; + var timerId1 = global.setInterval(function () { + global.clearInterval(timerId2); + }, 100); + + timerId2 = global.setInterval(fail, 100); + + clock.tick(400); + }); }); diff --git a/src/core/DelayedFunctionScheduler.js b/src/core/DelayedFunctionScheduler.js index 4756b323..91557a37 100644 --- a/src/core/DelayedFunctionScheduler.js +++ b/src/core/DelayedFunctionScheduler.js @@ -5,6 +5,7 @@ getJasmineRequireObj().DelayedFunctionScheduler = function() { var scheduledFunctions = {}; var currentTime = 0; var delayedFnCount = 0; + var deletedKeys = []; self.tick = function(millis, tickDate) { millis = millis || 0; @@ -51,6 +52,8 @@ getJasmineRequireObj().DelayedFunctionScheduler = function() { }; self.removeFunctionWithId = function(timeoutKey) { + deletedKeys.push(timeoutKey); + for (var runAtMillis in scheduledFunctions) { var funcs = scheduledFunctions[runAtMillis]; var i = indexOfFirstToPass(funcs, function (func) { @@ -126,7 +129,10 @@ getJasmineRequireObj().DelayedFunctionScheduler = function() { currentTime = newCurrentTime; - var funcsToRun = scheduledFunctions[currentTime]; + var funcsToRun = scheduledFunctions[currentTime].sort(function (a, b) { + return a.millis > b.millis; + }); + delete scheduledFunctions[currentTime]; forEachFunction(funcsToRun, function(funcToRun) { @@ -136,8 +142,13 @@ getJasmineRequireObj().DelayedFunctionScheduler = function() { }); forEachFunction(funcsToRun, function(funcToRun) { + if (deletedKeys.indexOf(funcToRun.timeoutKey) !== -1) { + // skip a timeoutKey deleted whilst we were running + return; + } funcToRun.funcToCall.apply(null, funcToRun.params || []); }); + deletedKeys = []; } while (scheduledLookup.length > 0 && // checking first if we're out of time prevents setTimeout(0) // scheduled in a funcToRun from forcing an extra iteration