11 Nov 2017, 15:00

Replicating JavaScripts setTimeout and setInterval in Go

In JavaScript we have two inbuilt functions for doing some work at some given point in the future. Firstly setTimeout; this allows us to run a function after a given period of time in milliseconds. We also have setInterval which allows us to run a function every x milliseconds until the interval is cancelled with clearInterval. You can find a solid overview of timers in JavaScript on MDN here.

For those unfamiliar with the two functions, let’s see how they look (in ES5):


    var timeout = 1000; // 1 second

    setTimeout(function(){
        console.log("This will happen after " + timeout + "milliseconds");
    });

    var interval = 500; // 0.5 seconds

    var anInterval = setInterval(function(){
        console.log("This will happen after " + timeout + "seconds");
    });

Recently I have been wanting to achieve the same thing in Go on a small side project I am working on. As such I thought I would share my findings with you all!

Replicating setInterval

setInterval is arguably the more complicated of the two functions to implement. The basic premise is we setup a new ticker from Go’s time package. The NewTicker function creates a channel which we can select over (see here for an overview of the select feature in Go), which passes a signal to the channel every x milliseconds, calling the arbitrary passed in function. We also select over the clear channel. This can in turn clear the interval when we pass a boolean value to it.


    func setInterval(someFunc func(), milliseconds int, async bool) chan bool {

        // How often to fire the passed in function 
        // in milliseconds
        interval := time.Duration(milliseconds) * time.Millisecond

        // Setup the ticket and the channel to signal
        // the ending of the interval
        ticker := time.NewTicker(interval)
        clear := make(chan bool)

        // Put the selection in a go routine
        // so that the for loop is none blocking
        go func() {
            for {

                select {
                case <-ticker.C:
                    if async {
                        // This won't block
                        go someFunc()
                    } else {
                        // This will block
                        someFunc()
                    }
                case <-clear:
                    ticker.Stop()
                    return
                }

            }
        }()

        // We return the channel so we can pass in 
        // a value to it to clear the interval
        return clear

    }

    func main() {

        // A counter for the number of times we print
        printed := 0
        
        // We call set interval to print Hello World forever
        // every 1 second
        interval := setInterval(func() {
            fmt.Println("Hello World")
            printed++
        }, 1000, false)
        
        // If we wanted to we had a long running task (i.e. network call)
        // we could pass in true as the last argument to run the function
        // as a goroutine

        // Some artificial work here to wait till we've printed
        // 5 times
        for {
            if printed == 5 {
                // Stop the ticket, ending the interval go routine
                stop <- true
                return
            }
        }

    }

Replicating setTimeout

Replicating JavaScripts setTimeout is slightly more straight forward. We can leverage the time package’s AfterFunc function which fires off goroutine that will run a function after a given period of time. We could do this using code like this:


    func setTimeout(someFunc func(), milliseconds int) {

        timeout := time.Duration(milliseconds) * time.Millisecond

        // This spawns a goroutine and therefore does not block
        time.AfterFunc(timeout, someFunc)

    }

    func main() {

        printed := false
        print := func() {
            fmt.Println("This will print after x milliseconds")
            printed = true
        }

        // Make the timeout print after 5 seconds
        setTimeout(print, 5000)

        fmt.Println("This will print straight away")
        
        // Wait until it's printed our function string
        // before we close the program
        for {
            if printed {
                return
            }
        }
        
    }

Hopefully this has given some insight on how we might achieve similar functionality in Go to JavaScripts setInterval and setTimeout functionality. If you see a potential problem, or have ideas about a better solution I would love to hear them!

22 Jul 2017, 11:30

Using Go to Plot Global Data

Wanting to explore more around geospatial tools with Go, this week I’ve been exploring Globe. Globe creates wireframe global visualisations of data sets, provided they contain latitude and longitude points. Here’s an example of the API from the README:

    shops, err := LoadCoffeeShops("./starbucks.json")
    if err != nil {
        log.Fatal(err)
    }

    green := color.NRGBA{0x00, 0x64, 0x3c, 192}
    g := globe.New()
    g.DrawGraticule(10.0)
    for _, s := range shops {
        g.DrawDot(s.Lat, s.Lng, 0.05, globe.Color(green))
    }
    g.CenterOn(40.645423, -73.903879)
    err = g.SavePNG("starbucks.png", 400)
    if err != nil {
        log.Fatal(err)
    }

I decided that I wanted to build a web service that would allow users to generate an image (PNG) using some sort of interface. With a few free evenings I got to work!

The Backend

For all request serving activity I used the built in http package. Go has a fantastic standard library for building web apps. However I did find it a little tough to find sensible examples of file upload handling.

For extracting the geospatial elements of the data, I used Paul Mach’s GeoJSON package for extracting latitude and longitudes from GeoJSON and then used the standard library CSV package for the doing the same operation with CSVs.

Finally for the drawing; we use the aforementioned Globe package to do the actual drawing. This was all relatively straightforward minus hitting one issue around trying to use RGBA colors rather than NRGBA (non-alpha-premultiplied 32-bit color) colors with the library. It turns out NRGBA produces a lot cleaner results, you can see the issue on the Globe package here.

The app is hosted on Heroku. It took me a little while to get to grips with deploying Go apps on the platform (leveraging Go’s vendoring system was a first for me), but apart from that it was pretty painless. The GitHub push-to-deploy integration is also a very nice touch!

The Frontend

The frontend uses jQuery (boooo) as a general DOM/request helper library. It would have been relatively straight forward to replace these with smaller modular libraries but I was feeling a tad on the lazy side (Pull Request anyone?). For the user interface we leverage Skeleton CSS for the layout.

Spectrum.js is used as the colour selector, which we pass to the backend to generate the dots. To get unique file names, I leveraged a UUID library that prevents clashes.

Lastly, and potentially most interestingly, we make use of Turf.js to find the centroid of the latitude/longitudes. For those of you who aren’t familiar Turf.js is a geospatial analysis library for JavaScript, with a very powerful set of tools at its disposal for dealing with geometries. Mapbox’s csv2geojson was also used to convert CSVs over to GeoJSON so that it can work alongside Turf.js (it uses GeoJSON as it’s operating format). Overall I feel like this was a pretty nice solution as it allows for ‘smart defaults’ of the center of where the image is generated, without the user having to guess/analyse there data extensively.

Demo

The service allows you to create images like this:

You can check out the app at https://globe-plotter.herokuapp.com and have a little play. Try this earthquake GeoJSON file from the USGS if you’re looking for and example.