Golang is a concurrent programming language. It has powerful features like and that can handle asynchronous tasks very well. Also, goroutines are not OS threads, and that's why you can spin up as many goroutines as you want without much overhead, it's stack size starts at only. So why ? Async/Await is a nice language feature that provides a simpler interface to asynchronous programming. Goroutines Channels 2KB async/await Project Link: https://github.com/Joker666/AsyncGoDemo How Does it Work? Started with F# and then C#, now in Python and Javascript, async/await is an extremely popular feature of a language. It simplifies the asynchronous method execution structure and, it reads like synchronous code. So much easier to follow for developers. Let's see a simple example in C# how async/await works Task Main(string[] args) { Console.WriteLine( ); done = DoneAsync(); Console.WriteLine( ); Console.WriteLine( done); } Task<int> DoneAsync() { Console.WriteLine( ); Task.Delay( ); Console.WriteLine( ); ; } static async "Let's start ..." var "Done is running ..." await static async "Warming up ..." await 3000 "Done ..." return 1 We have the function that would be executed when the program is run. We have which is an async function. We stop the execution of the code with function for 3 seconds. Delay is an async function itself, so we call it with await. Main DoneAsync Delay await only blocks the code execution within the async function In the main function, we do not call with await. But the execution starts for . Only when we await it, we get the result back. The execution flow looks like this DoneAsync DoneAsync Let 's start ... Warming up ... Done is running ... Done ... 1 This looks incredibly simple for asynchronous execution. Let's see how we can do it with Golang using Goroutines and Channels { r := ( ) fmt.Println( ) { time.Sleep( * time.Second) r <- fmt.Println( ) }() r } { fmt.Println( ) val := DoneAsync() fmt.Println( ) fmt.Println(<- val) } func DoneAsync () chan int make chan int "Warming up ..." go func () 3 1 "Done ..." return func main () "Let's start ..." "Done is running ..." Here, runs asynchronously and returns a channel. It writes a value to the channel once it's done executing the async task. In function, we invoke and keep doing our operations and then we read the value from the returned channel. It is a blocking call that waits till the value is written to the channel and after it gets the value it writes to the console. DoneAsync main DoneAsync Let 's start ... Warming up ... Done is running ... Done ... 1 We see, we achieve the same outcome as the C# program but it doesn't look as elegant as async/await. While this is actually good, we are able to do a lot more granular things with this approach much easily, we can also implement async/await keywords in Golang with a simple struct and interface. Let's try that. Implementing Async/Await The full code is available in the project link. To implement async/await in Golang, we will start with a package directory named . The project structure looks like async . ├── async │ └── async ├── main └── README.md .go .go In the async file, we write the simplest future interface that can handle async tasks. async Future { Await() {} } future { await {} } {} { f.await(context.Background()) } {}) Future { result {} c := ( {}) { (c) result = f() }() future{ await: {} { { <-ctx.Done(): ctx.Err() <-c: result } }, } } package import "context" // Future interface has the method signature for await type interface interface type struct func (ctx context.Context) interface func (f future) Await () interface return // Exec executes the async function func Exec (f () func interface var interface make chan struct go func () defer close return func (ctx context.Context) interface select case return case return Not a lot is happening here, we add a interface that has the method signature. Next, we add a struct that holds one value, a function signature of the function. Now struct implements interface's Await method by invoking its own function. Future Await future await futute Future await Next in the function, we execute the passed function asynchronously in goroutine. And we return the function. It waits for the channel to close or context to read from. Based on whichever happens first, it either returns the error or the result which is an interface. Exec await Now armed with this new async package, let's see how we can change our current go code { fmt.Println( ) time.Sleep( * time.Second) fmt.Println( ) } { fmt.Println( ) future := async.Exec( {} { DoneAsync() }) fmt.Println( ) val := future.Await() fmt.Println(val) } func DoneAsync () int "Warming up ..." 3 "Done ..." return 1 func main () "Let's start ..." func () interface return "Done is running ..." At the first glance, it looks much cleaner, we are not explicitly working with goroutine or channels here. Our function has been changed to a completely synchronous nature. In the main function, we use the package's method to handle . Which starts the execution of . The control flow is returned back to function which can execute other pieces of code. Finally, we make blocking call to and read back data. DoneAsync async Exec DoneAsync DoneAsync main Await Now the code looks much simpler and easier to read. We can modify our async package to incorporate a lot of other types of asynchronous tasks in Golang, but we would just stick to simple implementation for now in this tutorial. Conclusion We have gone through what async/await it and implemented a simple version of that in Golang. I would encourage you to look into async/await a lot more and see how it can ease the readability of the codebase much better.