We’ve learnt how we can use
syscall/js to create and manipulate DOM elements from our Go functions, but what if we want to treat Go like a library and execute a function to get a value back? Maybe we have an image processing library we want to use that was written in Go, or we want to use some functionality that our core business application has in it.
My first thought was to create a function that returns a value:
And immediately we hit an error because
js.FuncOf takes a signature of
func(args js.Value), meaning it takes a function that doesn’t return anything, a
void function (if you were to C# it).
Right, back to the drawing board.
In Node.js this is really prevalent:
We pass a function as the last argument that thats two arguments, and error object and the output of the function executing successfully. We’d then test if
err contained anything, throw if it does, continue if it doesn’t.
This then got me thinking, would it be so bad to implement that as a pattern when it comes to talking to Go? It seems logical, because we’re shelling out to another runtime, we shouldn’t have to wait for it to complete before we continue on in our application, we should treat it like an async operation.
Preparing Go for callbacks
Let’s start updating our Go code to handle the idea of this callback pattern. Since we’re given an array of
js.Value we’ll have to make an assumption about where the callback lives in that array. I’m going to follow how Node.js has done it and make an assumption that the last argument passed in was the callback function.
This is the same as doing
len Go function to take a subset of the array from the last item to the end (which will always be 1 item) and then grabbing that single value (since we get an array of length 1 and just need the value). Alternatively, you could write
inputs[len(inputs)-1], but I’m just experimenting with Go syntax and trying to learn what things do.
You might want to do a
Value.Type test against
Now that we have a
Value.Invoke, which is like
Value.Call that we saw in the last post but for use when you have a value that is a function, not an object that has a function.
Because I’m using the err/data style with the callback I’ll pass
null when there isn’t an error (you could also pass
undefined, pick your poison).
This results in our Go function looking like so:
Bonus - Promisifying Go
The callback pattern is fine, but it can lead to callback hell. It also means we can’t use the sexy new
Let’s just wrap it with a Promise!
Alternatively you could pass in the
reject callbacks and then Go makes a decision in which to invoke, but I prefer the callback pattern as it introduces less decision trees in the Go codebase.