gopkg.in/olebedev/go-duktape.v1@v1.0.0-20151008052556-e2ae92f01e4a/timers.go (about) 1 package duktape 2 3 import ( 4 "errors" 5 "fmt" 6 "time" 7 ) 8 9 // DefineTimers defines `setTimeout`, `clearTimeout`, `setInterval`, 10 // `clearInterval` into global context. 11 func (d *Context) DefineTimers() error { 12 d.PushGlobalStash() 13 // check if timers already exists 14 if !d.HasPropString(-1, "timers") { 15 d.PushObject() 16 d.PutPropString(-2, "timers") // stash -> [ timers:{} ] 17 d.Pop() 18 19 d.PushGlobalGoFunction("setTimeout", setTimeout) 20 d.PushGlobalGoFunction("setInterval", setInterval) 21 d.PushGlobalGoFunction("clearTimeout", clearTimeout) 22 d.PushGlobalGoFunction("clearInterval", clearTimeout) 23 return nil 24 } else { 25 d.Pop() 26 return errors.New("Timers are already defined") 27 } 28 } 29 30 func (d *Context) ResetTimers() { 31 d.PushGlobalStash() 32 d.PushObject() 33 d.PutPropString(-2, "timers") // stash -> [ timers:{} ] 34 d.Pop() 35 } 36 37 func setTimeout(c *Context) int { 38 id := c.pushTimer(0) 39 timeout := c.ToNumber(1) 40 if timeout < 1 { 41 timeout = 1 42 } 43 go func(id float64) { 44 <-time.After(time.Duration(timeout) * time.Millisecond) 45 c.Lock() 46 defer c.Unlock() 47 if c.duk_context == nil { 48 fmt.Println("[duktape] Warning!\nsetTimeout invokes callback after the context was destroyed.") 49 return 50 } 51 52 // check if timer still exists 53 c.putTimer(id) 54 if c.GetType(-1).IsObject() { 55 c.Pcall(0 /* nargs */) 56 } 57 c.dropTimer(id) 58 }(id) 59 c.PushNumber(id) 60 return 1 61 } 62 63 func clearTimeout(c *Context) int { 64 if c.GetType(0).IsNumber() { 65 c.dropTimer(c.GetNumber(0)) 66 c.Pop() 67 } 68 return 0 69 } 70 71 func setInterval(c *Context) int { 72 id := c.pushTimer(0) 73 timeout := c.ToNumber(1) 74 if timeout < 1 { 75 timeout = 1 76 } 77 go func(id float64) { 78 ticker := time.NewTicker(time.Duration(timeout) * time.Millisecond) 79 for _ = range ticker.C { 80 c.Lock() 81 // check if duktape context exists 82 if c.duk_context == nil { 83 c.dropTimer(id) 84 c.Pop() 85 ticker.Stop() 86 fmt.Println("[duktape] Warning!\nsetInterval invokes callback after the context was destroyed.") 87 c.Unlock() 88 continue 89 } 90 91 // check if timer still exists 92 c.putTimer(id) 93 if c.GetType(-1).IsObject() { 94 c.Pcall(0 /* nargs */) 95 c.Pop() 96 } else { 97 c.dropTimer(id) 98 c.Pop() 99 ticker.Stop() 100 } 101 c.Unlock() 102 } 103 }(id) 104 c.PushNumber(id) 105 return 1 106 } 107 108 func (d *Context) pushTimer(index int) float64 { 109 id := d.timerIndex.get() 110 111 d.PushGlobalStash() 112 d.GetPropString(-1, "timers") 113 d.PushNumber(id) 114 d.Dup(index) 115 d.PutProp(-3) 116 d.Pop2() 117 118 return id 119 } 120 121 func (d *Context) dropTimer(id float64) { 122 d.PushGlobalStash() 123 d.GetPropString(-1, "timers") 124 d.PushNumber(id) 125 d.DelProp(-2) 126 d.Pop2() 127 } 128 129 func (d *Context) putTimer(id float64) { 130 d.PushGlobalStash() // stash -> [ ..., timers: { <id>: { func: true } } ] 131 d.GetPropString(-1, "timers") // stash -> [ ..., timers: { <id>: { func: true } } }, { <id>: { func: true } ] 132 d.PushNumber(id) 133 d.GetProp(-2) // stash -> [ ..., timers: { <id>: { func: true } } }, { <id>: { func: true }, { func: true } ] 134 d.Replace(-3) 135 d.Pop() 136 }