Timers (setTimeout
and friends!) for RuntimeJS.
var timers = require('runtime-timers')
timers.install()
setInterval(function() {
console.log('hello!')
}, 100)
while(true) {
timers.update()
}
module.exports = { | |
update: update | |
, install: install | |
, setTimeout: globalTimeout | |
, setInterval: globalInterval | |
, setImmediate: globalImmediate | |
, clearTimeout: globalClearTimer | |
, clearInterval: globalClearTimer | |
, clearImmediate: globalClearTimer | |
} | |
function Timer(ms, cb, args) { | |
this._id = ++Timer.id | |
this._remainingMS = ms || 0 | |
this._cb = cb | |
this._args = args | |
this._next = null | |
this._prev = null | |
} | |
Timer.id = 0 | |
var insertTimer = defaultInsertTimer | |
var correctedAdd = 0 | |
var queued = [] | |
Timer.prototype.visit = function(fn, n) { | |
this._remainingMS -= n | |
fn(this) | |
if(this._next) { | |
this._next.visit(fn, n) | |
} | |
} | |
Timer.prototype.makeCallback = function() { | |
try { | |
if(typeof this._cb === 'string') { | |
return Function('return ' + this._cb)() | |
.apply(null, this._args) | |
} | |
this._cb.apply(null, this._args) | |
} catch(err) { | |
// what to do here? | |
} | |
this.remove() | |
} | |
Timer.prototype.remove = function() { | |
var last = this._prev | |
var next = this._next | |
if(last) { | |
last._next = next | |
} else { | |
currentTimer = next | |
} | |
if(next) { | |
next._prev = last | |
} | |
} | |
function install() { | |
setTimeout = globalTimeout | |
setInterval = globalInterval | |
setImmediate = globalImmediate | |
clearTimeout = globalClearTimer | |
clearInterval = globalClearTimer | |
clearImmediate = globalClearTimer | |
} | |
var lastMS = null | |
var currentTimer = null | |
function update() { | |
if(lastMS === null) { | |
lastMS = ticks() | |
return | |
} | |
var currentMS = ticks() | |
var delta = currentMS - lastMS | |
if(!delta) { | |
return | |
} | |
lastMS = currentMS | |
if(!currentTimer) { | |
return | |
} | |
insertTimer = deferredInsertTimer | |
queued.length = 0 | |
var current = currentTimer | |
currentTimer = null | |
try { | |
while(current) { | |
current._remainingMS -= delta | |
if(current._remainingMS <= 0) { | |
current.makeCallback() | |
} else if(currentTimer === null) { | |
currentTimer = current | |
} | |
current = current._next | |
} | |
} finally { | |
insertTimer = defaultInsertTimer | |
queued.forEach(insertTimer) | |
} | |
} | |
function deferredInsertTimer(timer) { | |
queued.push(timer) | |
} | |
function defaultInsertTimer(timer) { | |
var ms = timer._remainingMS | |
if(!currentTimer) { | |
currentTimer = timer | |
return | |
} | |
var current = currentTimer | |
, last = null | |
while(current && current._remainingMS < ms) { | |
last = current | |
current = current._next | |
} | |
if(last) { | |
var tmp = last._next | |
last._next = timer | |
timer._prev = last | |
timer._next = tmp | |
if(tmp) { | |
tmp._prev = timer | |
} | |
} else if(current) { | |
timer._next = current | |
current._prev = timer | |
if(current === currentTimer) { | |
currentTimer = timer | |
} | |
} | |
} | |
function globalTimeout(cb, ms /* args */) { | |
var args = [].slice.call(arguments, 2) | |
var timer = new Timer(ms, cb, args) | |
insertTimer(timer) | |
return timer._id | |
} | |
function globalInterval(cb, ms /* args */) { | |
var args = [].slice.call(arguments, 2) | |
var timer = new Timer(ms, _wrap, args) | |
insertTimer(timer) | |
return timer._id | |
function _wrap() { | |
try { | |
cb.apply(this, args) | |
} catch(err) { | |
print(err.stack) | |
} | |
setImmediate(function() { | |
timer._remainingMS = ms | |
insertTimer(timer) | |
}) | |
} | |
} | |
function globalImmediate(cb) { | |
return globalTimeout(cb, 0) | |
} | |
function globalClearTimer(id) { | |
var current = currentTimer | |
while(current) { | |
if(current._id === id) { | |
current.remove() | |
return | |
} | |
current = current._next | |
} | |
} |