Use advanced patterns in core.async
core.async is very powerful, but sometimes it can be hard to know exactly how to use it. Knowing the individual pieces is not enough. You've got to master the pieces, but then you've got to be thinking in terms of broader patterns. This course presents some powerful patterns, and hopefully points the way to using core.async in a more holistic way. I really like how little code it takes to build powerful concurrency patterns in core.async.
In fact, core.async has changed my code tremendously. It is great for mastering:
- complex timing, like timeouts, rate limiting, and starting multiple things at the same time
- preventing too much work from overloading your server
- and dealing with values that change over time
Want this course?Advanced Clojure: An Eric Normand Signature Course
Love it or leave it guarantee
If you don't learn as much from this course as you thought, just ask for a refund within 30 days and I'll give you your money back.
1. Take with Timeout
What if you want to take from a channel, but give up after a time? This is very useful when testing that something was put onto a channel. You can't wait forever, because then your tests will never finish. core.async makes this quite easy.
2. Put with Timeout
We saw how to take with a timeout. But how do you put with a timeout? If a channel is full, a put will block or park until it is empty. If that never happens, your code will never complete. core.async can do this as well.
I often have trouble figuring out when my go blocks fail and why. Exceptions are silently ignored. We can write a macro that captures the exception and passes it back out of the go block to the code where it is used.
4. Thread pool
When work tasks are being produced, we often want to handle them in a thread pool. This is exceedingly easy in core.async. Channels are queues, and we can use the
core.async/thread macro to create threads easily. If each loops through the tasks it gets from the queue, the work can be done in parallel.
Backpressure is incredibly important in a distrubuted environment. Our servers can face more load than they can handle. Web requests are cheaper to produce than to respond to. The best practice when faced with more requests than you can handle is to immediately respond that you can't handle it so that you can continue processing the requests you've already accepted.
6. Kill switch
On the JVM, there is no way to stop a thread from the outside. The same is true of core.async go processes. The only way to stop it is from the inside. A common way to kill multiple threads/go processes is to make a channel signal that they should die.
7. Poison pill
If you need finer control over which workers die, you could implement the poison pill pattern. In this one, a special value is considered to be a signal to die.
8. Start latch
When you need many go blocks to start work at very close to the same time, you can use a start latch. This pattern preps go blocks and signals them to start with the same channel.
9. Token bucket
Rate limiting is kind of hard to implement, especially across multiple threads. But in core.async, it's not hard at all. In this lesson, we implement a simple rate limiting algorithm called a token bucket.