Merge branch 'atscott-autoTick'
* Merges #2042 from @atscott * Fixes #1932 * Fixes #1725
This commit is contained in:
@@ -3066,6 +3066,15 @@ getJasmineRequireObj().Clock = function() {
|
|||||||
let installed = false;
|
let installed = false;
|
||||||
let delayedFunctionScheduler;
|
let delayedFunctionScheduler;
|
||||||
let timer;
|
let timer;
|
||||||
|
// Tracks how the clock ticking behaves.
|
||||||
|
// By default, the clock only ticks when the user manually calls a tick method.
|
||||||
|
// There is also an 'auto' mode which will advance the clock automatically to
|
||||||
|
// to the next task. Once enabled, there is currently no mechanism for users
|
||||||
|
// to disable the auto ticking.
|
||||||
|
let tickMode = {
|
||||||
|
mode: 'manual',
|
||||||
|
counter: 0
|
||||||
|
};
|
||||||
|
|
||||||
this.FakeTimeout = FakeTimeout;
|
this.FakeTimeout = FakeTimeout;
|
||||||
|
|
||||||
@@ -3175,8 +3184,95 @@ getJasmineRequireObj().Clock = function() {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Updates the clock to automatically advance time.
|
||||||
|
*
|
||||||
|
* With this mode, the clock advances to the first scheduled timer and fires it, in a loop.
|
||||||
|
* Between each timer, it will also break the event loop, allowing any scheduled promise
|
||||||
|
callbacks to execute _before_ running the next one.
|
||||||
|
*
|
||||||
|
* This mode allows tests to be authored in a way that does not need to be aware of the
|
||||||
|
* mock clock. Consequently, tests which have been authored without a mock clock installed
|
||||||
|
* can one with auto tick enabled without any other updates to the test logic.
|
||||||
|
*
|
||||||
|
* In many cases, this can greatly improve test execution speed because asynchronous tasks
|
||||||
|
* will execute as quickly as possible rather than waiting real time to complete.
|
||||||
|
*
|
||||||
|
* Furthermore, tests can be authored in a consitent manner. They can always be written in an asynchronous style
|
||||||
|
* rather than having `tick` sprinkled throughout the tests with mock time in order to manually
|
||||||
|
* advance the clock.
|
||||||
|
*
|
||||||
|
* When auto tick is enabled, `tick` can still be used to synchronously advance the clock if necessary.
|
||||||
|
*/
|
||||||
|
this.autoTick = function() {
|
||||||
|
if (tickMode.mode === 'auto') {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
tickMode = { mode: 'auto', counter: tickMode.counter + 1 };
|
||||||
|
advanceUntilModeChanges();
|
||||||
|
};
|
||||||
|
|
||||||
return this;
|
return this;
|
||||||
|
|
||||||
|
// Advances the Clock's time until the mode changes.
|
||||||
|
//
|
||||||
|
// The time is advanced asynchronously, giving microtasks and events a chance
|
||||||
|
// to run before each timer runs.
|
||||||
|
//
|
||||||
|
// @function
|
||||||
|
// @return {!Promise<undefined>}
|
||||||
|
async function advanceUntilModeChanges() {
|
||||||
|
if (!installed) {
|
||||||
|
throw new Error(
|
||||||
|
'Mock clock is not installed, use jasmine.clock().install()'
|
||||||
|
);
|
||||||
|
}
|
||||||
|
const { counter } = tickMode;
|
||||||
|
|
||||||
|
while (true) {
|
||||||
|
await newMacrotask();
|
||||||
|
|
||||||
|
if (
|
||||||
|
tickMode.counter !== counter ||
|
||||||
|
!installed ||
|
||||||
|
delayedFunctionScheduler === null
|
||||||
|
) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!delayedFunctionScheduler.isEmpty()) {
|
||||||
|
delayedFunctionScheduler.runNextQueuedFunction(function(millis) {
|
||||||
|
mockDate.tick(millis);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Waits until a new macro task.
|
||||||
|
//
|
||||||
|
// Used with autoTick(), which is meant to act when the test is waiting, we need
|
||||||
|
// to insert ourselves in the macro task queue.
|
||||||
|
//
|
||||||
|
// @return {!Promise<undefined>}
|
||||||
|
async function newMacrotask() {
|
||||||
|
// MessageChannel ensures that setTimeout is not throttled to 4ms.
|
||||||
|
// https://developer.mozilla.org/en-US/docs/Web/API/setTimeout#reasons_for_delays_longer_than_specified
|
||||||
|
// https://stackblitz.com/edit/stackblitz-starters-qtlpcc
|
||||||
|
// Note: This trick does not work in Safari, which will still throttle the setTimeout
|
||||||
|
const channel = new MessageChannel();
|
||||||
|
await new Promise(resolve => {
|
||||||
|
channel.port1.onmessage = resolve;
|
||||||
|
channel.port2.postMessage(undefined);
|
||||||
|
});
|
||||||
|
channel.port1.close();
|
||||||
|
channel.port2.close();
|
||||||
|
// setTimeout ensures that we interleave with other setTimeouts.
|
||||||
|
await new Promise(resolve => {
|
||||||
|
realTimingFunctions.setTimeout.call(global, resolve);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
function originalTimingFunctionsIntact() {
|
function originalTimingFunctionsIntact() {
|
||||||
return (
|
return (
|
||||||
global.setTimeout === realTimingFunctions.setTimeout &&
|
global.setTimeout === realTimingFunctions.setTimeout &&
|
||||||
@@ -3407,6 +3503,37 @@ getJasmineRequireObj().DelayedFunctionScheduler = function(j$) {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// Returns whether there are any scheduled functions.
|
||||||
|
// Returns true if there are any scheduled functions, otherwise false.
|
||||||
|
this.isEmpty = function() {
|
||||||
|
return this.scheduledFunctions_.length === 0;
|
||||||
|
};
|
||||||
|
|
||||||
|
// Runs the next timeout in the queue, advancing the clock.
|
||||||
|
this.runNextQueuedFunction = function(tickDate) {
|
||||||
|
if (this.scheduledLookup_.length === 0) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const newCurrentTime = this.scheduledLookup_[0];
|
||||||
|
if (newCurrentTime >= this.currentTime_) {
|
||||||
|
tickDate(newCurrentTime - this.currentTime_);
|
||||||
|
this.currentTime_ = newCurrentTime;
|
||||||
|
}
|
||||||
|
|
||||||
|
const funcsAtTime = this.scheduledFunctions_[this.currentTime_];
|
||||||
|
const fn = funcsAtTime.shift();
|
||||||
|
if (funcsAtTime.length === 0) {
|
||||||
|
delete this.scheduledFunctions_[this.currentTime_];
|
||||||
|
this.scheduledLookup_.splice(0, 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (fn.recurring) {
|
||||||
|
this.reschedule_(fn);
|
||||||
|
}
|
||||||
|
fn.funcToCall.apply(null, fn.params || []);
|
||||||
|
};
|
||||||
|
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -687,6 +687,142 @@ describe('Clock (acceptance)', function() {
|
|||||||
expect(recurring1.calls.count()).toBe(4);
|
expect(recurring1.calls.count()).toBe(4);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
describe('auto tick mode', () => {
|
||||||
|
let delayedFunctionScheduler;
|
||||||
|
let mockDate;
|
||||||
|
let clock;
|
||||||
|
|
||||||
|
beforeEach(() => {
|
||||||
|
delayedFunctionScheduler = new jasmineUnderTest.DelayedFunctionScheduler();
|
||||||
|
mockDate = {
|
||||||
|
install: function() {},
|
||||||
|
tick: function() {},
|
||||||
|
uninstall: function() {}
|
||||||
|
};
|
||||||
|
clock = new jasmineUnderTest.Clock(
|
||||||
|
// We use the real window for global or firefox is displeased when we try to call a real setTimeout on an object "that doesn't implement window".
|
||||||
|
typeof window !== 'undefined' ? window : { setTimeout: setTimeout },
|
||||||
|
function() {
|
||||||
|
return delayedFunctionScheduler;
|
||||||
|
},
|
||||||
|
mockDate
|
||||||
|
);
|
||||||
|
clock.install();
|
||||||
|
clock.autoTick();
|
||||||
|
});
|
||||||
|
|
||||||
|
afterEach(() => {
|
||||||
|
clock.uninstall();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('can run setTimeouts/setIntervals asynchronously', function() {
|
||||||
|
const recurring = jasmine.createSpy('recurring'),
|
||||||
|
fn1 = jasmine.createSpy('fn1'),
|
||||||
|
fn2 = jasmine.createSpy('fn2'),
|
||||||
|
fn3 = jasmine.createSpy('fn3');
|
||||||
|
|
||||||
|
const intervalId = clock.setInterval(recurring, 50);
|
||||||
|
// In a microtask, add some timeouts.
|
||||||
|
Promise.resolve()
|
||||||
|
.then(function() {
|
||||||
|
return new Promise(function(resolve) {
|
||||||
|
clock.setTimeout(resolve, 25);
|
||||||
|
});
|
||||||
|
})
|
||||||
|
.then(function() {
|
||||||
|
fn1();
|
||||||
|
return new Promise(function(resolve) {
|
||||||
|
clock.setTimeout(resolve, 200);
|
||||||
|
});
|
||||||
|
})
|
||||||
|
.then(function() {
|
||||||
|
fn2();
|
||||||
|
return new Promise(function(resolve) {
|
||||||
|
clock.setTimeout(resolve, 100);
|
||||||
|
});
|
||||||
|
})
|
||||||
|
.then(function() {
|
||||||
|
fn3();
|
||||||
|
});
|
||||||
|
|
||||||
|
expect(recurring).not.toHaveBeenCalled();
|
||||||
|
expect(fn1).not.toHaveBeenCalled();
|
||||||
|
expect(fn2).not.toHaveBeenCalled();
|
||||||
|
expect(fn3).not.toHaveBeenCalled();
|
||||||
|
|
||||||
|
return new Promise(resolve => clock.setTimeout(resolve, 50))
|
||||||
|
.then(function() {
|
||||||
|
expect(recurring).toHaveBeenCalledTimes(1);
|
||||||
|
expect(fn1).toHaveBeenCalled();
|
||||||
|
expect(fn2).not.toHaveBeenCalled();
|
||||||
|
expect(fn3).not.toHaveBeenCalled();
|
||||||
|
|
||||||
|
return new Promise(resolve => clock.setTimeout(resolve, 175));
|
||||||
|
})
|
||||||
|
.then(function() {
|
||||||
|
expect(recurring).toHaveBeenCalledTimes(4);
|
||||||
|
expect(fn1).toHaveBeenCalled();
|
||||||
|
expect(fn2).toHaveBeenCalled();
|
||||||
|
expect(fn3).not.toHaveBeenCalled();
|
||||||
|
|
||||||
|
clock.clearInterval(intervalId);
|
||||||
|
return new Promise(resolve => clock.setTimeout(resolve, 100));
|
||||||
|
})
|
||||||
|
.then(function() {
|
||||||
|
expect(recurring).toHaveBeenCalledTimes(4);
|
||||||
|
expect(fn1).toHaveBeenCalled();
|
||||||
|
expect(fn2).toHaveBeenCalled();
|
||||||
|
expect(fn3).toHaveBeenCalled();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('speeds up the execution of the timers in all browsers', async () => {
|
||||||
|
const startTimeMs = performance.now() / 1000;
|
||||||
|
await new Promise(resolve => clock.setTimeout(resolve, 5000));
|
||||||
|
await new Promise(resolve => clock.setTimeout(resolve, 5000));
|
||||||
|
await new Promise(resolve => clock.setTimeout(resolve, 5000));
|
||||||
|
await new Promise(resolve => clock.setTimeout(resolve, 5000));
|
||||||
|
const endTimeMs = performance.now() / 1000;
|
||||||
|
// Ensure we didn't take 20s to complete the awaits above and, in fact, can do it in a fraction of a second
|
||||||
|
expect(endTimeMs - startTimeMs).toBeLessThan(100);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('avoids throttling in browsers other than Safari', async () => {
|
||||||
|
if (
|
||||||
|
typeof navigator !== 'undefined' &&
|
||||||
|
/^((?!chrome|android|firefox).)*safari/i.test(navigator.userAgent)
|
||||||
|
) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
// This test ensures the setTimeout loop isn't getting throttled by browsers
|
||||||
|
const promises = [];
|
||||||
|
// 2000 timers at ~4ms throttling = 8_000ms would time out if we weren't
|
||||||
|
// preventing the throttle with the MessageChannel trick.
|
||||||
|
for (let i = 0; i < 2000; i++) {
|
||||||
|
promises.push(new Promise(resolve => clock.setTimeout(resolve)));
|
||||||
|
}
|
||||||
|
const startTimeMs = performance.now() / 1000;
|
||||||
|
await Promise.all(promises);
|
||||||
|
const endTimeMs = performance.now() / 1000;
|
||||||
|
expect(endTimeMs - startTimeMs).toBeLessThan(1000);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('is easy to test async functions with interleaved timers and microtasks', async () => {
|
||||||
|
async function blackBoxWithLotsOfAsyncStuff() {
|
||||||
|
await new Promise(r => clock.setTimeout(r, 10));
|
||||||
|
await Promise.resolve();
|
||||||
|
await Promise.resolve();
|
||||||
|
await new Promise(r => clock.setTimeout(r, 20));
|
||||||
|
await Promise.resolve();
|
||||||
|
await Promise.resolve();
|
||||||
|
await Promise.resolve();
|
||||||
|
return 'done';
|
||||||
|
}
|
||||||
|
const result = await blackBoxWithLotsOfAsyncStuff();
|
||||||
|
expect(result).toBe('done');
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
it('can clear a previously set timeout', function() {
|
it('can clear a previously set timeout', function() {
|
||||||
const clearedFn = jasmine.createSpy('clearedFn'),
|
const clearedFn = jasmine.createSpy('clearedFn'),
|
||||||
delayedFunctionScheduler = new jasmineUnderTest.DelayedFunctionScheduler(),
|
delayedFunctionScheduler = new jasmineUnderTest.DelayedFunctionScheduler(),
|
||||||
|
|||||||
@@ -264,6 +264,42 @@ describe('DelayedFunctionScheduler', function() {
|
|||||||
expect(fn).toHaveBeenCalled();
|
expect(fn).toHaveBeenCalled();
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it('runs the next scheduled funtion', function() {
|
||||||
|
const scheduler = new jasmineUnderTest.DelayedFunctionScheduler();
|
||||||
|
const fn = jasmine.createSpy('fn');
|
||||||
|
const tickSpy = jasmine.createSpy('tick');
|
||||||
|
|
||||||
|
scheduler.scheduleFunction(fn, 10, [], false, 'foo');
|
||||||
|
|
||||||
|
expect(fn).not.toHaveBeenCalled();
|
||||||
|
|
||||||
|
scheduler.runNextQueuedFunction(tickSpy);
|
||||||
|
|
||||||
|
expect(fn).toHaveBeenCalled();
|
||||||
|
expect(tickSpy).toHaveBeenCalledWith(10);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('runs the only a single scheduled funtion in a time slot', function() {
|
||||||
|
const scheduler = new jasmineUnderTest.DelayedFunctionScheduler();
|
||||||
|
const fn1 = jasmine.createSpy('fn');
|
||||||
|
const fn2 = jasmine.createSpy('fn2');
|
||||||
|
const tickSpy = jasmine.createSpy('tick');
|
||||||
|
|
||||||
|
scheduler.scheduleFunction(fn1, 10, [], false, 'foo1');
|
||||||
|
scheduler.scheduleFunction(fn2, 10, [], false, 'foo2');
|
||||||
|
|
||||||
|
scheduler.runNextQueuedFunction(tickSpy);
|
||||||
|
|
||||||
|
expect(fn1).toHaveBeenCalled();
|
||||||
|
expect(fn2).not.toHaveBeenCalled();
|
||||||
|
expect(tickSpy).toHaveBeenCalledWith(10);
|
||||||
|
|
||||||
|
tickSpy.calls.reset();
|
||||||
|
scheduler.runNextQueuedFunction(tickSpy);
|
||||||
|
expect(fn2).toHaveBeenCalled();
|
||||||
|
expect(tickSpy).toHaveBeenCalledWith(0);
|
||||||
|
});
|
||||||
|
|
||||||
it('updates the mockDate per scheduled time', function() {
|
it('updates the mockDate per scheduled time', function() {
|
||||||
const scheduler = new jasmineUnderTest.DelayedFunctionScheduler(),
|
const scheduler = new jasmineUnderTest.DelayedFunctionScheduler(),
|
||||||
tickDate = jasmine.createSpy('tickDate');
|
tickDate = jasmine.createSpy('tickDate');
|
||||||
|
|||||||
@@ -29,6 +29,15 @@ getJasmineRequireObj().Clock = function() {
|
|||||||
let installed = false;
|
let installed = false;
|
||||||
let delayedFunctionScheduler;
|
let delayedFunctionScheduler;
|
||||||
let timer;
|
let timer;
|
||||||
|
// Tracks how the clock ticking behaves.
|
||||||
|
// By default, the clock only ticks when the user manually calls a tick method.
|
||||||
|
// There is also an 'auto' mode which will advance the clock automatically to
|
||||||
|
// to the next task. Once enabled, there is currently no mechanism for users
|
||||||
|
// to disable the auto ticking.
|
||||||
|
let tickMode = {
|
||||||
|
mode: 'manual',
|
||||||
|
counter: 0
|
||||||
|
};
|
||||||
|
|
||||||
this.FakeTimeout = FakeTimeout;
|
this.FakeTimeout = FakeTimeout;
|
||||||
|
|
||||||
@@ -138,8 +147,95 @@ getJasmineRequireObj().Clock = function() {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Updates the clock to automatically advance time.
|
||||||
|
*
|
||||||
|
* With this mode, the clock advances to the first scheduled timer and fires it, in a loop.
|
||||||
|
* Between each timer, it will also break the event loop, allowing any scheduled promise
|
||||||
|
callbacks to execute _before_ running the next one.
|
||||||
|
*
|
||||||
|
* This mode allows tests to be authored in a way that does not need to be aware of the
|
||||||
|
* mock clock. Consequently, tests which have been authored without a mock clock installed
|
||||||
|
* can one with auto tick enabled without any other updates to the test logic.
|
||||||
|
*
|
||||||
|
* In many cases, this can greatly improve test execution speed because asynchronous tasks
|
||||||
|
* will execute as quickly as possible rather than waiting real time to complete.
|
||||||
|
*
|
||||||
|
* Furthermore, tests can be authored in a consitent manner. They can always be written in an asynchronous style
|
||||||
|
* rather than having `tick` sprinkled throughout the tests with mock time in order to manually
|
||||||
|
* advance the clock.
|
||||||
|
*
|
||||||
|
* When auto tick is enabled, `tick` can still be used to synchronously advance the clock if necessary.
|
||||||
|
*/
|
||||||
|
this.autoTick = function() {
|
||||||
|
if (tickMode.mode === 'auto') {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
tickMode = { mode: 'auto', counter: tickMode.counter + 1 };
|
||||||
|
advanceUntilModeChanges();
|
||||||
|
};
|
||||||
|
|
||||||
return this;
|
return this;
|
||||||
|
|
||||||
|
// Advances the Clock's time until the mode changes.
|
||||||
|
//
|
||||||
|
// The time is advanced asynchronously, giving microtasks and events a chance
|
||||||
|
// to run before each timer runs.
|
||||||
|
//
|
||||||
|
// @function
|
||||||
|
// @return {!Promise<undefined>}
|
||||||
|
async function advanceUntilModeChanges() {
|
||||||
|
if (!installed) {
|
||||||
|
throw new Error(
|
||||||
|
'Mock clock is not installed, use jasmine.clock().install()'
|
||||||
|
);
|
||||||
|
}
|
||||||
|
const { counter } = tickMode;
|
||||||
|
|
||||||
|
while (true) {
|
||||||
|
await newMacrotask();
|
||||||
|
|
||||||
|
if (
|
||||||
|
tickMode.counter !== counter ||
|
||||||
|
!installed ||
|
||||||
|
delayedFunctionScheduler === null
|
||||||
|
) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!delayedFunctionScheduler.isEmpty()) {
|
||||||
|
delayedFunctionScheduler.runNextQueuedFunction(function(millis) {
|
||||||
|
mockDate.tick(millis);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Waits until a new macro task.
|
||||||
|
//
|
||||||
|
// Used with autoTick(), which is meant to act when the test is waiting, we need
|
||||||
|
// to insert ourselves in the macro task queue.
|
||||||
|
//
|
||||||
|
// @return {!Promise<undefined>}
|
||||||
|
async function newMacrotask() {
|
||||||
|
// MessageChannel ensures that setTimeout is not throttled to 4ms.
|
||||||
|
// https://developer.mozilla.org/en-US/docs/Web/API/setTimeout#reasons_for_delays_longer_than_specified
|
||||||
|
// https://stackblitz.com/edit/stackblitz-starters-qtlpcc
|
||||||
|
// Note: This trick does not work in Safari, which will still throttle the setTimeout
|
||||||
|
const channel = new MessageChannel();
|
||||||
|
await new Promise(resolve => {
|
||||||
|
channel.port1.onmessage = resolve;
|
||||||
|
channel.port2.postMessage(undefined);
|
||||||
|
});
|
||||||
|
channel.port1.close();
|
||||||
|
channel.port2.close();
|
||||||
|
// setTimeout ensures that we interleave with other setTimeouts.
|
||||||
|
await new Promise(resolve => {
|
||||||
|
realTimingFunctions.setTimeout.call(global, resolve);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
function originalTimingFunctionsIntact() {
|
function originalTimingFunctionsIntact() {
|
||||||
return (
|
return (
|
||||||
global.setTimeout === realTimingFunctions.setTimeout &&
|
global.setTimeout === realTimingFunctions.setTimeout &&
|
||||||
|
|||||||
@@ -87,6 +87,37 @@ getJasmineRequireObj().DelayedFunctionScheduler = function(j$) {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// Returns whether there are any scheduled functions.
|
||||||
|
// Returns true if there are any scheduled functions, otherwise false.
|
||||||
|
this.isEmpty = function() {
|
||||||
|
return this.scheduledFunctions_.length === 0;
|
||||||
|
};
|
||||||
|
|
||||||
|
// Runs the next timeout in the queue, advancing the clock.
|
||||||
|
this.runNextQueuedFunction = function(tickDate) {
|
||||||
|
if (this.scheduledLookup_.length === 0) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const newCurrentTime = this.scheduledLookup_[0];
|
||||||
|
if (newCurrentTime >= this.currentTime_) {
|
||||||
|
tickDate(newCurrentTime - this.currentTime_);
|
||||||
|
this.currentTime_ = newCurrentTime;
|
||||||
|
}
|
||||||
|
|
||||||
|
const funcsAtTime = this.scheduledFunctions_[this.currentTime_];
|
||||||
|
const fn = funcsAtTime.shift();
|
||||||
|
if (funcsAtTime.length === 0) {
|
||||||
|
delete this.scheduledFunctions_[this.currentTime_];
|
||||||
|
this.scheduledLookup_.splice(0, 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (fn.recurring) {
|
||||||
|
this.reschedule_(fn);
|
||||||
|
}
|
||||||
|
fn.funcToCall.apply(null, fn.params || []);
|
||||||
|
};
|
||||||
|
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user