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:
AntonioVdlC / await-on
✋🏽 - Await on asynchronous operations
await-on
Go-like error handling for async JavaScript functions.
Installation
This package is distributed via npm:
npm install @antoniovdlc/await-on
Motivation
Async functions in JavaScript are great! They allow you to write asynchronous code as if it were synchronous.
The main drawback I personally experience is having to write code like this:
try {
const result = await someFunc()
// Do something with `result`
} catch (err) {
// Handle error
}
Having had some past experience using Go, and after some time to fully understand the elegance of its simplistic error handling approach, it felt right to try to replicate it:
result, err := someFunc();
if err != nil {
// Handle error
}
// Do something with `result`
This is why this package exists, so that we can write asynchronous JavaScript code in a style as close as possible to that of Go:
const
…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
statements. 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:
AntonioVdlC / defer
⏭ - Defer statements in functions
defer
Go-like defer functions in JavaScript.
Installation
This package is distributed via npm:
npm install @antoniovdlc/defer
Motivation
Go provides the very interesting concept of defer
ing functions until the end of a function's execution.
package main
import "fmt"
func main() {
defer fmt.Println("world")
fmt.Println("hello")
}
// hello
// world
Such built-in construct might be very useful in JavaScript for example, where we sometimes need to do some clean-up, and thus could potentially co-locate it with the instanciation.
Usage
You can use this library either as an ES module or a CommonJS package:
import { defer, deferrable } from "@antoniovdlc/defer";
- or -
const { defer, deferrable } = require("@antoniovdlc/defer");
defer(fn: Function, caller: Function) : void
defer
takes a function as argument, which will be called at the end of the execution of…
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.
Which idioms and constructs in other languages would you like to see in JavaScript?
Top comments (0)