Go by Contrast
Goroutines & channels
Lesson 4 of 5
What you'll learn
- Understand goroutines as cheap, runtime-scheduled concurrency
- See how channels pass values safely between goroutines
- Contrast with JS's single-threaded event loop and Promises
JavaScript is single-threaded: concurrency means an event loop interleaving callbacks and awaited Promises. Go is genuinely concurrent. A goroutine is a function running independently, scheduled by the Go runtime onto real OS threads. They're cheap — you can launch thousands — and you start one by prefixing a call with go.
go doWork() // runs concurrently; the caller keeps going
fmt.Println("kept going")
But how do goroutines communicate without data races? With channels — typed pipes you send values into and receive values out of.
ch := make(chan string)
go func() {
ch <- "result" // send (blocks until someone receives)
}()
msg := <-ch // receive (blocks until someone sends)
fmt.Println(msg) // "result"
The closest TS analogy is a Promise plus an async queue, but the model is different — channels are synchronous handoffs by default:
async function doWork(): Promise<string> {
return "result";
}
const msg = await doWork();
console.log(msg);
Share memory by communicating
Go's mantra is "Do not communicate by sharing memory; share memory by communicating." Instead of multiple goroutines locking a shared variable, one goroutine owns the data and others send it messages over a channel. Ordering is preserved: values come out of a channel in the order they went in.
A producer/consumer loop is the canonical shape — producers ch <- v and a consumer ranges over the channel until it's closed:
for v := range ch { // receives until ch is closed
process(v)
}
Channels block
An unbuffered channel blocks the sender until a receiver is ready (and vice versa). That's a feature — it synchronizes goroutines — but forget to receive and the sender hangs forever. This is Go's most common beginner deadlock.
The challenge is a JS model of a channel: a queue where producers enqueue values and a consumer drains them in order. Real Go would block and schedule across goroutines; here we model just the ordered handoff.
Run it. This models producers sending into a channel and a consumer receiving the values in order.
What happens when a goroutine sends on an unbuffered channel with no receiver ready?
Next: the data structures and standard library that make Go productive out of the box.
Saved on this device. Sign in to sync your progress everywhere.