Core.async FAQs

Back to SIG Clojure Tutorials

Author: Gary Johnson

Date: 2021-07-12

1. What is the difference between a normal thread and an IOC thread?

An IOC (Inversion Of Control) thread doesn't need to use additional system resources beyond those of the main program thread. Instead, the main thread enters a `go` block and executes the code up to the first parking or blocking expression (e.g., !, !!).

If a parking expression is encountered and no value is available on the channel, the main thread is released to execute other parts of the program. When a value becomes available on the channel, the `go` block will recover control of the main thread and resume execution from the parking expression.

If a blocking expression is encountered and no value is available on the channel, the main thread is blocked until a value becomes available on the channel. When a value becomes available on the channel the main thread resumes execution from the blocking expression.

Note that parking expressions may only be executed within `go` blocks, but blocking expressions do not have this requirement. In general, you should prefer `go` + parking expressions over blocking expressions whenever possible. If blocking expressions are required for your algorithm, you will need to perform your thread management manually using other Clojure functions like `future` or `thread`.

2. Within a JS context/process, how do the IOC threads work/what is the benefit of using one if there's only one thread?

JS contexts are really where core.async shines. Here, we can use `go` blocks and parking expressions (which only use the main thread's resources) to write code that reads synchronously but executes asynchronously. In short, this means we can write client-side code without callbacks, which both reduces the number of lines of code we have to write and maintain and significantly simplifies our program logic.

3. What is a buffer in the context of core.async?

Threads communicate with one another over channels in core.async. You can think of a channel as a pipe whose length is `buffer`. If a channel's buffer size is `N`, you can put `N` values onto the channel before the next put expression will park or block.

4. What is the difference between an unbuffered and a buffered channel?

With an unbuffered channel, your first put expression will park or block until another thread's take expression pulls the value off the channel. In this case, you can think of a channel as a porthole. The putter opens the porthole and tries to hand a value through it. If no taker is on the other side to grab the value from their hand, the putter just keeps standing there. Once a taker grabs the value from the putter's hand, the putter can close the porthole. Hopefully that metaphor is sufficiently intuitive for you to visualize the process.