In Go, does it make sense to write non-blocking code?

coming from node.js point of view, where all code is non-blocking.

In Go, non-blocking is easily achieved using channels.

if one were writing a node.js type server in go, does it make sense to make it non-blocking? for example, having a database connect() function return a channel, as versus blocking while waiting for the connection to occur.

to me, this seems the correct approach

but ...


Blocking and non-blocking aren't really about performance, they are about an interface. If you have a single thread of execution then a blocking call prevents your program from doing any useful work while it's waiting. But if you have multiple threads of execution a blocking call doesn't really matter because you can just leave that thread blocked and do useful work in another.

In Go, a goroutine is swapped out for another one when it blocks on I/O. The Go runtime uses non-blocking I/O syscalls to avoid the operating system blocking the thread so a different goroutine can be run on it while the first is waiting for it's I/O.

Goroutines are really cheap so writing non-blocking style code is not needed.


Write blocking functions. The language allows you to easily turn a synchronous call into an asynchronous one.

If you want to call a function asynchronously, use a go statement. Something like this:

c := make(chan bool)
go func() {
    blockingFunction()
    c <- true
}()

// do some other stuff here while the blocking function runs

// wait for the blocking function to finish if it hasn't already
<-c

In Go, system calls are implemented in a non-blocking way using the most efficient underlying mechanism that the OS supports (e.g. epoll). If you have no other code to run while you wait for the result of a call, then it blocks the thread (for lack of a better thing to do), but if you have alternate goroutines active, then they will run instead.

Callbacks (as you're used to using in js) allow for essentially the same underlying mechanics, but with arguably more mental gymnastics necessary for the programmer.

In Go, your code to run after a function call is specified immediately following the function call rather than defined as a callback. Code that you want to run parallel to an execution path should be wrapped in a goroutine, with communication through channels.


For typical web-server type applications, I would recommend not making everything asynchronous. There are a few reasons.

  • It's easier to reason about serial blocking code than async code (easier to see bugs)

  • golang error handling is based on defer(), panic(), and recover(), which probably won't give you what you want with 100% asynchronous code

  • Goroutines can leak if you're not careful [one discussion]. The more async behavior you have, the harder it becomes to track down these types of problems and the more likely they are to show up.

One strategy is to focus the asynchonicity at a high level and leave everything else blocking. So you might have a "database handler" blob that is logically distinct from the "request handler" blob. They both run in separate goroutines and communicate using channels. But within the "database handler", the calls to establish a database connection and execute each query are blocking.

You don't have to choose 100% asynchronous or 0% asynchronous.


Blocking interfaces are always simpler and better than non-blocking ones. The beauty of Go is that it allows you to write concurrent (and parallel) code in a simple, and easy to reason about, blocking style.

The fashion for non-blocking programming is all due to deficiencies in the languages people are using (specially JavaScript), not because non-blocking programming is intrinsically better.