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();iferr!=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{constresult=awaitsomeFunc()// 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]=awaiton(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:
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{constresult=awaitsomeFunc()// 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();
iferr!=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:
Another fairly neat Go feature is defer statements, which allow for some functions to only be called right before their caller function returns.
packagemainimport"fmt"funcmain(){deferfmt.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");functiongetData(){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");functiongetData(){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:
functiondefer(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.
Which would then yield the following construction:
const{client}=require("./db");constgetData=deferrable(asyncfunctionfn(){awaitclient.connect();defer(()=>client.close(),fn);// Do things with `client` ...}
You can have a closer look at the complete implementation at:
Go provides the very interesting concept of defering functions until the end of a function's execution.
package main
import"fmt"funcmain() {
deferfmt.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:
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?