Most of my professional career has revolved around JavaScript, but as a programming language enthusiast, I particularly enjoy learning about new languages. After playing a bit with Go, there were a few constructs that I felt would be useful in JavaScript as well. Here are two such constructs and some libraries I've implemented to bring them into JavaScript.
Error handling
Love it or hate it, Golang's approach to error handling is simple and straightforward.
result, err := someFunc();
if err != nil {
// Handle error
}
// Do something with `result`
A place within JavaScript code where this style could particularly shine, is in regards to asynchronous code, where most of the times the following code is written to handle potential errors:
try {
const result = await someFunc()
// Do something with `result`
} catch (err) {
// Handle error
}
There is nothing wrong with that particular idiom, but would it be more elegant to have a simple and less nested way to handle errors from asynchronous functions? Maybe something inspired by Go's error handling idiom like the following:
const [result, err] = await on(someFunc)
if (err != null) {
// Handle error
}
// Do something with `result`
To achieve that construct, you can look into the following package I've recently publish: @antoniovdlc/await-on, or any of the similar packages. At its core, the implementation of the library really revolves around these few lines of code:
async function on(fn) {
try {
const result = await fn();
return [result, null];
} catch (error) {
return [null, error];
}
}
export default on;
You can have a closer look at the complete implementation at: https://github.com/AntonioVdlC/await-on.
Learn more about error handling in Go: https://tour.golang.org/methods/19.
Defer statements
Another fairly neat Go feature is defer
statements, which allow for some functions to only be called right before their caller function returns.
package main
import "fmt"
func main() {
defer fmt.Println("world")
fmt.Println("hello")
}
// Prints:
// hello
// world
This construct is useful for releasing resources after being processed. This might be for example a connection to database, or reading from a file, or any clean-up operation we'd like to perform. By using defer
statements it is easier to co-locate the allocation and de-allocation of resources.
For example, instead of writing code similar to:
const { client } = require("./db");
function getData() {
client.connect();
// Do things with `client` ...
// /!\ Don't forget to close the connection /!\
client.close();
}
We could technically co-locate the calls to client.connect()
and client.close()
as follow:
const { client } = require("./db");
function getData() {
client.connect();
defer(() => client.close());
// Do things with `client` ...
// We are now sure the call to `client.close()` will be called once the body of the function has done executing.
}
The implementation here was a little bit more tricky that for the error handling construct. As such, there is a stark difference between @antoniovdlc/defer and Go's defer
statements is the order of execution of the statements (Go goes for a last-in-first-out approach, while the package linked goes for a first-in-first-out approach).
This allows us to use the following trick for synchronous functions:
function defer(fn) {
setTimeout(fn, 0);
}
But the above code isn't really that interesting per se.
The real trick comes with asynchronous functions! Here a wrapper function, and an Array were needed to be able to track and call all the defer
statement. The defer
function also needs to be passed a second argument, the caller function, due to the deprecation of Function.caller.
function deferrable(fn) {
const f = async () => {
const result = await fn();
for (let i = 0, length = fn.__$_deferArr.length; i < length; i++) {
await fn.__$_deferArr[i]();
}
return result;
};
return f;
}
function defer(fn, caller) {
if (!Array.isArray(caller.__$_deferArr)) {
caller.__$_deferArr = [];
}
caller.__$_deferArr.push(fn);
}
Which would then yield the following construction:
const { client } = require("./db");
const getData = deferrable(async function fn() {
await client.connect();
defer(() => client.close(), fn);
// Do things with `client` ...
}
You can have a closer look at the complete implementation at: https://github.com/AntonioVdlC/defer.
Learn more about defer statements: https://tour.golang.org/flowcontrol/12.
Looking from inspiration at Go, and implementing some of its idioms in JavaScript was an interesting exercise, and will maybe hopefully be helpful to someone.