doodle/pkg/scripting/timers.go
Noah Petherbridge 4d08bf1d85 Switch JavaScript engine to goja
* Switch from otto to goja for JavaScript engine.
* goja supports many ES6 syntax features like arrow functions,
  const/let, for-of with more coming soon.
* Same great features as otto, more modern environment for doodads!
2022-01-16 20:09:27 -08:00

109 lines
2.5 KiB
Go

package scripting
import (
"time"
"github.com/dop251/goja"
)
// Timer keeps track of delayed function calls for the scripting engine.
type Timer struct {
id int
callback goja.Value
interval time.Duration // milliseconds delay for timeout
next time.Time // scheduled time for next invocation
repeat bool // for setInterval
}
/*
SetTimeout registers a callback function to be run after a while.
This is to be called by JavaScript running in the VM and has an API similar to
that found in web browsers.
The callback is a JavaScript function and the interval is in milliseconds,
with 1000 being 'one second.'
Returns the ID number of the timer in case you want to clear it. The underlying
Timer type is NOT exposed to JavaScript.
*/
func (vm *VM) SetTimeout(callback goja.Value, interval int) int {
return vm.AddTimer(callback, interval, false)
}
/*
SetInterval registers a callback function to be run repeatedly.
Returns the ID number of the timer in case you want to clear it. The underlying
Timer type is NOT exposed to JavaScript.
*/
func (vm *VM) SetInterval(callback goja.Value, interval int) int {
return vm.AddTimer(callback, interval, true)
}
/*
AddTimer loads timeouts and intervals into the VM's memory and returns the ID.
*/
func (vm *VM) AddTimer(callback goja.Value, interval int, repeat bool) int {
// Get the next timer ID. The first timer has ID 1.
vm.timerLastID++
id := vm.timerLastID
t := &Timer{
id: id,
callback: callback,
interval: time.Duration(interval),
repeat: repeat,
}
t.Schedule()
vm.timers[id] = t
return id
}
// TickTimer checks if any timers are ready and calls their functions.
func (vm *VM) TickTimer(now time.Time) {
if len(vm.timers) == 0 {
return
}
// IDs of expired timeouts to clear.
var clear []int
for id, timer := range vm.timers {
if now.After(timer.next) {
if function, ok := goja.AssertFunction(timer.callback); ok {
function(goja.Undefined())
}
if timer.repeat {
timer.Schedule()
} else {
clear = append(clear, id)
}
}
}
// Clean up expired timers.
if len(clear) > 0 {
for _, id := range clear {
delete(vm.timers, id)
}
}
}
/*
ClearTimer will clear both timeouts and intervals.
In the JavaScript VM this function is bound to clearTimeout() and clearInterval()
to expose an API like that seen in web browsers.
*/
func (vm *VM) ClearTimer(id int) {
delete(vm.timers, id)
}
// Schedule the callback to be run in the future.
func (t *Timer) Schedule() {
t.next = time.Now().Add(t.interval * time.Millisecond)
}