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  }