Merge pull request #466 from
maciej-filip-sz:DelayedFunctionScheduler-patch Sets and executes timeouts set during a tick.
This commit is contained in:
@@ -983,14 +983,17 @@ getJasmineRequireObj().Clock = function() {
|
|||||||
getJasmineRequireObj().DelayedFunctionScheduler = function() {
|
getJasmineRequireObj().DelayedFunctionScheduler = function() {
|
||||||
function DelayedFunctionScheduler() {
|
function DelayedFunctionScheduler() {
|
||||||
var self = this;
|
var self = this;
|
||||||
|
var scheduledLookup = [];
|
||||||
var scheduledFunctions = {};
|
var scheduledFunctions = {};
|
||||||
var currentTime = 0;
|
var currentTime = 0;
|
||||||
var delayedFnCount = 0;
|
var delayedFnCount = 0;
|
||||||
|
|
||||||
self.tick = function(millis) {
|
self.tick = function(millis) {
|
||||||
millis = millis || 0;
|
millis = millis || 0;
|
||||||
currentTime = currentTime + millis;
|
var endTime = currentTime + millis;
|
||||||
runFunctionsWithinRange(currentTime - millis, currentTime);
|
|
||||||
|
runScheduledFunctions(endTime);
|
||||||
|
currentTime = endTime;
|
||||||
};
|
};
|
||||||
|
|
||||||
self.scheduleFunction = function(funcToCall, millis, params, recurring, timeoutKey, runAtMillis) {
|
self.scheduleFunction = function(funcToCall, millis, params, recurring, timeoutKey, runAtMillis) {
|
||||||
@@ -1006,7 +1009,8 @@ getJasmineRequireObj().DelayedFunctionScheduler = function() {
|
|||||||
millis = millis || 0;
|
millis = millis || 0;
|
||||||
timeoutKey = timeoutKey || ++delayedFnCount;
|
timeoutKey = timeoutKey || ++delayedFnCount;
|
||||||
runAtMillis = runAtMillis || (currentTime + millis);
|
runAtMillis = runAtMillis || (currentTime + millis);
|
||||||
scheduledFunctions[timeoutKey] = {
|
|
||||||
|
var funcToSchedule = {
|
||||||
runAtMillis: runAtMillis,
|
runAtMillis: runAtMillis,
|
||||||
funcToCall: f,
|
funcToCall: f,
|
||||||
recurring: recurring,
|
recurring: recurring,
|
||||||
@@ -1014,58 +1018,73 @@ getJasmineRequireObj().DelayedFunctionScheduler = function() {
|
|||||||
timeoutKey: timeoutKey,
|
timeoutKey: timeoutKey,
|
||||||
millis: millis
|
millis: millis
|
||||||
};
|
};
|
||||||
|
|
||||||
|
if (runAtMillis in scheduledFunctions) {
|
||||||
|
scheduledFunctions[runAtMillis].push(funcToSchedule);
|
||||||
|
} else {
|
||||||
|
scheduledFunctions[runAtMillis] = [funcToSchedule];
|
||||||
|
scheduledLookup.push(runAtMillis);
|
||||||
|
scheduledLookup.sort(function (a, b) {
|
||||||
|
return a - b;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
return timeoutKey;
|
return timeoutKey;
|
||||||
};
|
};
|
||||||
|
|
||||||
self.removeFunctionWithId = function(timeoutKey) {
|
self.removeFunctionWithId = function(timeoutKey) {
|
||||||
delete scheduledFunctions[timeoutKey];
|
for (var runAtMillis in scheduledFunctions) {
|
||||||
|
var funcs = scheduledFunctions[runAtMillis];
|
||||||
|
var i = indexOfFirstToPass(funcs, function (func) {
|
||||||
|
return func.timeoutKey === timeoutKey;
|
||||||
|
});
|
||||||
|
|
||||||
|
if (i > -1) {
|
||||||
|
if (funcs.length === 1) {
|
||||||
|
delete scheduledFunctions[runAtMillis];
|
||||||
|
deleteFromLookup(runAtMillis);
|
||||||
|
} else {
|
||||||
|
funcs.splice(i, 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
// intervals get rescheduled when executed, so there's never more
|
||||||
|
// than a single scheduled function with a given timeoutKey
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
self.reset = function() {
|
self.reset = function() {
|
||||||
currentTime = 0;
|
currentTime = 0;
|
||||||
|
scheduledLookup = [];
|
||||||
scheduledFunctions = {};
|
scheduledFunctions = {};
|
||||||
delayedFnCount = 0;
|
delayedFnCount = 0;
|
||||||
};
|
};
|
||||||
|
|
||||||
return self;
|
return self;
|
||||||
|
|
||||||
// finds/dupes functions within range and removes them.
|
function indexOfFirstToPass(array, testFn) {
|
||||||
function functionsWithinRange(startMillis, endMillis) {
|
var index = -1;
|
||||||
var fnsToRun = [];
|
|
||||||
for (var timeoutKey in scheduledFunctions) {
|
|
||||||
var scheduledFunc = scheduledFunctions[timeoutKey];
|
|
||||||
if (scheduledFunc &&
|
|
||||||
scheduledFunc.runAtMillis >= startMillis &&
|
|
||||||
scheduledFunc.runAtMillis <= endMillis) {
|
|
||||||
|
|
||||||
// remove fn -- we'll reschedule later if it is recurring.
|
for (var i = 0; i < array.length; ++i) {
|
||||||
self.removeFunctionWithId(timeoutKey);
|
if (testFn(array[i])) {
|
||||||
if (!scheduledFunc.recurring) {
|
index = i;
|
||||||
fnsToRun.push(scheduledFunc); // schedules each function only once
|
break;
|
||||||
} else {
|
|
||||||
fnsToRun.push(buildNthInstanceOf(scheduledFunc, 0));
|
|
||||||
var additionalTimesFnRunsInRange =
|
|
||||||
Math.floor((endMillis - scheduledFunc.runAtMillis) / scheduledFunc.millis);
|
|
||||||
for (var i = 0; i < additionalTimesFnRunsInRange; i++) {
|
|
||||||
fnsToRun.push(buildNthInstanceOf(scheduledFunc, i + 1));
|
|
||||||
}
|
|
||||||
reschedule(buildNthInstanceOf(scheduledFunc, additionalTimesFnRunsInRange));
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return fnsToRun;
|
return index;
|
||||||
}
|
}
|
||||||
|
|
||||||
function buildNthInstanceOf(scheduledFunc, n) {
|
function deleteFromLookup(key) {
|
||||||
return {
|
var value = Number(key);
|
||||||
runAtMillis: scheduledFunc.runAtMillis + (scheduledFunc.millis * n),
|
var i = indexOfFirstToPass(scheduledLookup, function (millis) {
|
||||||
funcToCall: scheduledFunc.funcToCall,
|
return millis === value;
|
||||||
params: scheduledFunc.params,
|
});
|
||||||
millis: scheduledFunc.millis,
|
|
||||||
recurring: scheduledFunc.recurring,
|
if (i > -1) {
|
||||||
timeoutKey: scheduledFunc.timeoutKey
|
scheduledLookup.splice(i, 1);
|
||||||
};
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function reschedule(scheduledFn) {
|
function reschedule(scheduledFn) {
|
||||||
@@ -1077,21 +1096,30 @@ getJasmineRequireObj().DelayedFunctionScheduler = function() {
|
|||||||
scheduledFn.runAtMillis + scheduledFn.millis);
|
scheduledFn.runAtMillis + scheduledFn.millis);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function runScheduledFunctions(endTime) {
|
||||||
function runFunctionsWithinRange(startMillis, endMillis) {
|
if (scheduledLookup.length === 0 || scheduledLookup[0] > endTime) {
|
||||||
var funcsToRun = functionsWithinRange(startMillis, endMillis);
|
|
||||||
if (funcsToRun.length === 0) {
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
funcsToRun.sort(function(a, b) {
|
do {
|
||||||
return a.runAtMillis - b.runAtMillis;
|
currentTime = scheduledLookup.shift();
|
||||||
});
|
|
||||||
|
|
||||||
for (var i = 0; i < funcsToRun.length; ++i) {
|
var funcsToRun = scheduledFunctions[currentTime];
|
||||||
var funcToRun = funcsToRun[i];
|
delete scheduledFunctions[currentTime];
|
||||||
funcToRun.funcToCall.apply(null, funcToRun.params || []);
|
|
||||||
}
|
for (var i = 0; i < funcsToRun.length; ++i) {
|
||||||
|
var funcToRun = funcsToRun[i];
|
||||||
|
funcToRun.funcToCall.apply(null, funcToRun.params || []);
|
||||||
|
|
||||||
|
if (funcToRun.recurring) {
|
||||||
|
reschedule(funcToRun);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} while (scheduledLookup.length > 0 &&
|
||||||
|
// checking first if we're out of time prevents setTimeout(0)
|
||||||
|
// scheduled in a funcToRun from forcing an extra iteration
|
||||||
|
currentTime !== endTime &&
|
||||||
|
scheduledLookup[0] <= endTime);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user