JavaScript Surprises to a Go Developer

12 February 2019

The scope of var is wrong

Never use var to declare variables, use let instead.


== is conversion and comparison

What you really need is ===, which is similar to comparing two interface{}.

=== is shallow

{a:1}==={a:1} is false, while Go struct with string are compared by contents (but not for slice).

Also this affects map key comparison. So object key is not so useful in JS as struct key in Go. The equality test is always on the references rather than the contents.


const works for objects, but the contents are modifiable


Single and double quotes are the same


Back quotes are like text/template

Escapes also works in back quotes and more template related features.


Switch cases fall through by default

Access undeclared object property returns undefined

Trying to access undeclared variables throws an exception but accessing undeclared object properties return a value called undefined.

Use 'property' in obj to check if a property is declared or not.

Only float64 is supported

Object is like map[string]interface{}

Array is like map[int]interface{}

Map is like map[interface{}]interface{}

for...in traverses properties recursively

for...of is like for...range in Go


    console.log({}.constructor === {}['__proto__'].constructor);
    console.log({}.constructor.prototype === {}['__proto__']);

Promise can be chained and composed

It is quite impressive how JS do concurrency differently and efficiently.

Promise.then(onFulfilled) where onFufilled can return another Promise.

Promise.all([func1(), func2(), func3()])
.then(([result1, result2, result3]) => { /* use result1, result2 and result3 */ });

Computed property name

    [prop]: 42,

There are two types of generator method syntax

    *gen() {

    gen: function*() {


Run: Graceful Goroutine Orchestration

14 December 2018



While Go provides goroutines, channels and selects as first-class citizens to support concurrent programming, it is not trivial to combine these elements to address important concerns of goroutine orchestration, e.g. error handling, panic recovery, goroutine leak prevention, goroutine reuse, goroutine throttle and logging.

The package provides a mini-framework to address those cross-cutting concerns.

Quick start

go get -u h12.io/run

Here is an example illustrating the usage of the goroutine pool and the group. The task is described in the “Google Search 2.0” page from this slide.

// the goroutine pool
pool := run.NewGoroutinePool(
	run.Max(8),                // the pool contains maximum 8 goroutines
	run.IdleTime(time.Minute), // a goroutine will stay in idle for maximum 1 minute before exiting

// the group
// the goroutine pool might have longer lifespan than the group
group := run.NewGroup(
	context.Background(), // a context that can cancel the whole group
	run.Pool(pool),       // the goroutine pool used by the group
	run.Recover(true),    // recover from panic and returns the PanicError
	run.Log(func(info *run.LogInfo) { // a log function for all starts/stops

searches := []*GoogleSearch{
	{Search: Web, Query: "golang"},
	{Search: Image, Query: "golang"},
	{Search: Video, Query: "golang"},
for _, search := range searches {
	// start searching in parallel
	if err := group.Go(search); err != nil {

// wait for all searches stop
if err := group.Wait(); err != nil {

for _, search := range searches {

See the full example here.


The package is built around the concept of a runner.

type Runner interface {
	Run(context.Context) error

A correct implementation of a runner should satisify the following conditions:

  • blocks when the work is on going
  • returns when all work is done, an error occurred or context is cancelled

With goroutine pool and group in the package, the user does not need to use the go statement explicitly, but only needs to implement their objects satisfying the Runner interface.

A Group is useful when multiple concurrent sub-tasks needed to be combined as a single task (the task failed when one of them failed, every sub-task should be cancelled when the task is cancelled).

A Pool is useful when there are many short-lived gorotuines.

Group can be built upon pool, not vice versa.

How to Start Godoc on Mac

5 May 2018


godoc -http=:6060 &; disown (pidof godoc)


godoc -http=:6060 &; disown `pidof godoc`

And pidof can be installed with Homebrew.

Tmux Cheatsheet on Mac

20 April 2018

Create a new session with iTerm2 integration

tmux -CC new -s [session-name]

Attach to a session with iTerm2 integration

tmux -CC attach -t [session-name]

How to Diff two JSON Files

10 April 2018

Just sort the keys first!


cat a.json | jq --sort-keys . > aa.json
cat b.json | jq --sort-keys . > bb.json
vimdiff aa.json bb.json

Pagination Done Right

28 February 2018

Server side pagination is intrinsically not accurate, as long as the data is dynamic.

The data items could be inserted, deleted or changed on the server side while the user goes forward and backward among the pages.

However, there is an algorithm that can keep the pagination as stable as possible:

  1. encode the id and sorting fields of last value in a page as the continue-token
  2. return the continue-token along with each page
  3. the client must pass the continue-token to fetch the next page
  4. the next page starts with value > continue-token || (value == continue-token && value.id > continue-token.id)


buid: Bipartite Unique Identifier

15 November 2017

A BUID is a 128-bit unique ID composed of two 64-bit parts: shard and key.

It is not only a unique ID, but also contains the sharding information, so that the messages with the same BUID could be stored together within the same DB shard.

Also, when a message is stored in a shard, the shard part of the BUID can be trimmed off to save the space, and only the key part needs to be stored as the primary key.

Bigendian is chosen to make each part byte-wise lexicographic sortable.

The string representation uses basex 62 encoding.