That is indeed the birds eye view or what is going on.
The essential problem is how to do this (in pseudo code)
loop forever
read keyboard
do something with keyboard input
loop forever
read network stream
do something with network input
loop forever
wait for some time
do some timed processing
...
...
Where all of those reads and waits hang up (block) the flow of execution until some data arrives or event happens.
That structures our code nicely but how do we get on with the second and third loops when we are stuck in the first one?
Traditionally we would do this by running each of those loops in a thread. Then our OS kernel will take care of changing context from one thread to another, when some data or event happens the relevant thread becomes ready to run and program flow is directed there by the kernel. When a loop is blocked waiting for data the kernel can run other threads instead if they are ready.
This threading requires a lot of state to be maintained by the kernel for each thread, the program counter, the stack, etc. Which is wasteful. It also requires a lot of time for swapping conntect from thread to thread.
"green threads", "goroutines" etc are a way to do this threading within the application, without the heavy weight mechanism of kernel threads. Smaller stacks, less processor state saved, no kernel calls, whatever I don't know exactly.
The whole async thing tackles this by chopping what looks like regular code up into lots of pieces that will get called as and when events happen so that the pieces can process the data arriving with that event.
Of course this means that if you do actually write
loop {
...
}
In your code you will not just have hung up that particular async "thread" but likely your entire program!
Unlike the situation with real threads.
Someone correct me of I'm way off the mark in this description.
.