import {AssertionError} from '$qui/base/errors.js'
/**
* A convenience class that makes it easy to work with timeouts.
* @alias qui.base.Timer
*/
class Timer {
/**
* @constructs
* @param {Number} [defaultTimeout] a default timeout, in milliseconds
* @param {Function} [onTimeout] an optional timeout callback
* @param {Boolean} [repeat] set to `true` to automatically restart timer on timeout
*/
constructor(defaultTimeout = null, onTimeout = null, repeat = false) {
this._defaultTimeout = defaultTimeout
this._onTimeout = onTimeout
this._repeat = repeat
this._cancelled = false
this._startedTime = null
this._timeoutHandle = null
}
/**
* Start the timer.
* @param {Number} [timeout] a timeout, in milliseconds, that overrides the timer's `defaultTimeout`
*/
start(timeout = null) {
if (this._timeoutHandle) {
throw new AssertionError('Timer already started')
}
if (timeout == null) {
timeout = this._defaultTimeout
}
if (timeout == null) {
throw new AssertionError('Cannot start timer without timeout')
}
this._cancelled = false
this._startedTime = new Date().getTime()
this._timeoutHandle = setTimeout(function () {
this._timeoutHandle = null
if (this._repeat) {
this.start(timeout)
}
if (this._onTimeout) {
this._onTimeout()
}
}.bind(this), timeout)
}
/**
* Cancel the timer.
*/
cancel() {
if (!this._timeoutHandle) {
throw new AssertionError('Timer not started')
}
if (this._cancelled) {
throw new AssertionError('Timer already cancelled')
}
clearTimeout(this._timeoutHandle)
this._timeoutHandle = null
this._cancelled = true
this._startedTime = null
}
/**
* Start the timer if it's not currently running. Otherwise cancel and start it over.
* @param {Number} [timeout] a timeout, in milliseconds, that overrides the timer's `defaultTimeout`
*/
restart(timeout = null) {
if (this.isRunning()) {
this.cancel()
}
this.start(timeout)
}
/**
* Tell if the timer is currently running.
* @returns {Boolean}
*/
isRunning() {
return !!this._timeoutHandle
}
/**
* Tell if the timer has been cancelled.
* @returns {Boolean}
*/
isCancelled() {
return this._cancelled
}
/**
* Tell if the timer has fired.
* @returns {Boolean}
*/
isFired() {
return !this._timeoutHandle && !!this._startedTime
}
/**
* Tell the remaining time, in milliseconds.
* @returns {Number}
*/
getRemainingTime() {
if (this.isRunning()) {
throw new AssertionError('Timer not started')
}
return new Date().getTime() - this._startedTime
}
}
export default Timer