Async → Goroutines
Concurrency patterns
Introduction
In this lesson, you'll learn about async → goroutines in Go. Coming from JavaScript, you already have a foundation for understanding this concept. We'll build on that knowledge while highlighting the key differences.
In JavaScript, you're familiar with concurrency patterns.
Go has its own approach to concurrency patterns, which we'll explore step by step.
The Go Way
Let's see how Go handles this concept. Here's a typical example:
package main
import "sync"
// Goroutine — lightweight thread
go processItem(id)
// Channel for results
results := make(chan string, len(ids))
var wg sync.WaitGroup
for _, id := range ids {
wg.Add(1)
go func(id int) {
defer wg.Done()
results <- fetchData(id)
}(id)
}
// Wait and collect
go func() {
wg.Wait()
close(results)
}()
for r := range results {
fmt.Println(r)
}Comparing to JavaScript
Here's how you might have written similar code in JavaScript:
// Promise-based async
async function fetchData(url) {
const res = await fetch(url);
return res.json();
}
// Parallel
const [users, posts] = await Promise.all([
fetchData("/api/users"),
fetchData("/api/posts"),
]);
// Sequential
for (const id of ids) {
await processItem(id);
}You may be used to different syntax or behavior.
Go goroutines are OS-scheduled; JS async is event-loop based
You may be used to different syntax or behavior.
Go channels for communication; JS uses Promises and callbacks
You may be used to different syntax or behavior.
Go can truly run code in parallel on multiple CPUs; JS is single-threaded
You may be used to different syntax or behavior.
sync.WaitGroup replaces Promise.all for multiple goroutines
Step-by-Step Breakdown
1. go Keyword
The 'go' keyword starts a goroutine — a lightweight concurrent function. Unlike async/await, it doesn't block the caller.
await processItem(id);go processItem(id) // concurrent, non-blocking2. Channels
Channels pass data safely between goroutines — the Go equivalent of resolving a Promise.
ch := make(chan int)
go func() { ch <- compute() }()
result := <-ch // blocks until value arrives3. WaitGroup vs Promise.all
sync.WaitGroup tracks when all goroutines complete, equivalent to awaiting Promise.all.
await Promise.all(ids.map(id => process(id)))var wg sync.WaitGroup
for _, id := range ids { wg.Add(1); go func(id int){ defer wg.Done(); process(id) }(id) }
wg.Wait()Common Mistakes
When coming from JavaScript, developers often make these mistakes:
- Go goroutines are OS-scheduled; JS async is event-loop based
- Go channels for communication; JS uses Promises and callbacks
- Go can truly run code in parallel on multiple CPUs; JS is single-threaded
Key Takeaways
- go keyword starts a goroutine (like fire-and-forget async)
- Channels replace Promises for inter-goroutine communication
- WaitGroup replaces Promise.all
- Go is truly parallel; JS is single-threaded