Recently, I built a lightweight task scheduler on top of in Go. The application schedules batch jobs with custom arguments and schedules as single containers via AWS ECS, an orchestrator built on top of EC2 and Docker. One of the key requirements of the scheduler is monitoring the status of actively running tasks. For this, I chose to use AWS ECS’s endpoint, which takes ECS task ID(s) and returns their status, exit code if complete, reason for stopping, etc. AWS ECS DescribeTasks Due to rate limits, I had to batch API calls. Initially, I thought my code would look something like this — Seems reasonable, right? The scheduler just has to create a monitor, submit tasks via , and poll until the target task is complete. Watch GetStatus … Sorta. The first problem with is the scheduler must know the monitor is batched since it returns a map of all tasks to their status. Exposing implementation details in an API is generally a sign of a leaky abstraction. GetStatus Take #2— Great! Now, returns a status given a task. Though just a few extra characters, this is a huge usability improvement since the scheduler no longer needs to batch tasks. GetStatus However, there’s still a problem with this code. When should scheduler call ? In a loop, of course! GetStatus That works, but it’s not elegant and less than ideal for more expensive operations. We can do better. If you came to Go from JavaScript, Scala, C#, or another language with Promises/Futures, you’re probably thinking “How do those translate to Go?”. Well, Go doesn’t have “futures” per se, but it also doesn’t them because channels are so powerful. Let’s take a peek! need This is beautiful, efficient, and way simpler. returns a channel, which is “await”-able. Boom! monitor.Watch(task) Using channels as in Go isn’t perfect. First, it’s not possible to make a library around them with helper functions as seen in , for example, due to Go’s lack of generics. Also, creating a channel for every call is not space-efficient, as each channel has its own mutex, buffer, etc., so this isn’t the right decision if you’re dealing with lots of calls in a performance-intensive application. In those cases, you should use a single channel and reader, e.g. futures Bluebird function … but that comes with more indirection so try out channels as futures when possible (most use cases) for a readable, straight-forward API! If you found this interesting, consider following me on , where I share my experiences with Go amongst other things. Twitter is how hackers start their afternoons. We’re a part of the family. We are now and happy to opportunities. Hacker Noon @AMI accepting submissions discuss advertising &sponsorship To learn more, , , or simply, read our about page like/message us on Facebook tweet/DM @HackerNoon. If you enjoyed this story, we recommend reading our and . Until next time, don’t take the realities of the world for granted! latest tech stories trending tech stories