I love this about Go. All functions are simple and synchronous, but if you want to call them concurrently, just "go SlowThing()" and coordinate with a channel. Compare that to the async stuff in C#, Python, etc -- it's bolted on later, and you see double of everything.

You're just using the channel as a future.

None of this is really problematic at the top level view of things. But when you need to compose libraries or applications that make use of these things - yes, even channels in Go - you can start running into problems.

Especially if you don't actually control the process you're running in. This is why promises and async/await really exist. E.g. if you have code you need to fork off the main UI thread so you don't block it, but need to re-capture the UI thread so you can call UI update code when you're done with your long running task. async/await is so much cleaner here.

Async/await allow for much more complex control of flow than simple goroutines and channels. Sometimes it's necessary. Sometimes it isn't. It was necessary in nodejs. It's extremely useful in applications where you have a "main thread" that drives your application that you don't want to unnecessarily block. Go doesn't solve this problem out-the-box. There's a reason why c# adopted async/await despite having futures and all sorts of other tools.

I don’t see how futures can be more flexible than go routines. Could you explain that more? And why couldn’t you just spawn a goroutine to avoid blocking your main thread?

Sometimes you want to block the main thread, or at least finish what you were doing. With async/await and cooperative concurrency you can be explicit about what will run on a thread. You retain control of the thread until you yield or await. You can ask tasks to complete on other threads or post back to the main thread. You have a lot of control. Its easy to write code without locks that runs concurrently on the main thread but is still able to build a UI in a threaded way.

I don't really know how go UI frameworks work. How do you have multiple, preemptively scheduled goroutines on the UI thread but without critical sections? You have to use channels and message passing back to a main thread manager to handle this, yes?

I think Java's Loom would have the same issue but again, I don't really know. Perhaps worrying about a UI thread is 'fighting the last war' and we should work on new UI paradigms in these new language features.

You can spawn a goroutine that immediately waits on a channel, so that it will not actually do anything until you want it to. This seems at least as expressive as a single-threaded switch() primitive. I think it can also express the threaded async pattern you want, but I'm not sure.

Are there any popular, idiomatic Go UI frameworks I could explore?

You can try your luck with https://github.com/avelino/awesome-go#gui

I explored the landscape earlier this year when I was building a GUI version of a CLI tool written in Go. I was throughly disappointed by all the “native Go” and non-webview options — narrow selection of widgets with basic functionality missing, UIs ranging from lack of polish to horrendous looking (that makes Java GUIs look like godsend), lots of bugs, etc. I ended up using the Qt binding which, despite its own share of problems, at least worked fairly reliably and didn’t constantly get in my way in every way imaginable: https://github.com/therecipe/qt