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