Run the test suite with the leakchecker library
❯ go test -v
=== RUN   TestSelecting
done checking leak
--- PASS: TestSelecting (11.30s)
=== RUN   TestLeakyAsync
    TestLeakyAsync: leaktest.go:132: leaktest: timed out checking goroutines
    TestLeakyAsync: leaktest.go:150: leaktest: leaked goroutine: goroutine 25 [chan receive]:
        perform.LeakyAsync.func1(0xc00008c1e0)
        	/Users/jmcbride/workspace/channels-testing/perform.go:37 +0x34
        created by perform.LeakyAsync
        	/Users/jmcbride/workspace/channels-testing/perform.go:36 +0x3f
--- FAIL: TestLeakyAsync (5.57s)
=== RUN   TestContextAsync
--- PASS: TestContextAsync (0.57s)
Run the benchmarks with bench and benchmem to see performance
❯ go test -v -bench=.  -benchmem -run "Bench*"
goos: darwin
goarch: amd64
pkg: perform
BenchmarkSelecting
BenchmarkSelecting-8      	       1	10114375732 ns/op	     104 B/op	       2 allocs/op
BenchmarkLeakyAsync
BenchmarkLeakyAsync-8     	       2	 585489776 ns/op	     704 B/op	       3 allocs/op
BenchmarkContextAsync
BenchmarkContextAsync-8   	       2	 570398894 ns/op	     976 B/op	       9 allocs/op
PASS
ok  	perform	13.655s
LeakyAsync is roughly 2 times faster. But fails the leak checker test as the goroutine is not resolved.
Selecting is slow because it performs a select on every iteration of the for loop.
ContextAsync is the best of both worlds. We don't have to do a select within the for loop, yet we avoid a go routine
leak.