It's been a while since I did much Go, but I think you can handle this cleanly by making one channel per task, and having an array of channels similar to the array of promises. Each channel takes that task's result, then closes. The caller waits on each channel in sequence.
That's exactly what the author ends up with, too. It's also how you might handle this in any other language that allows for some form of "join", either intended or usable as a join. Waiting on promises, thread joins, channels (as a signal mechanism), it's all the same pattern: Instantiate a bunch of asynchronous activities and queue up a "handler" corresponding to it, wait on the handlers in your desired order.