Fix #1426 clearTimeout not correctly clearing a timeout

clearTimeout was not correctly handling the case of clearing a
timeout that is also scheduled to run at the same tick.

This fix adds a deletedKeys array that is checked whilst we are
running the scheduled functions for the current clock tick. If
a function exists in deletedKeys it will not be ran. deletedKeys
is then reset to an empty array.
This commit is contained in:
Michael Leaney
2017-10-11 20:54:56 +08:00
parent c1957ecd7c
commit 516e00d7ba
2 changed files with 32 additions and 5 deletions

View File

@@ -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);
});
});

View File

@@ -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