There are a number of ways to make our application work more in an async manner. The easiest way
to do this is to add more Event variants to our existing EventHandler. Specifically, we would
like to only render in the main run loop when we receive a Event::Render variant:
Another thing I personally like to do is combine the EventHandler struct and the Terminal
functionality. To do this, we are going to rename our EventHandler struct to a Tui struct. We
are also going to include a few more Event variants for making our application more capable.
Below is the relevant snippet of an updated Tui struct. You can click on the “Show hidden lines”
button at the top right of the code block or check out
this section of the book for the full version
The key things to note are that we create a tick_interval, render_interval and reader stream
that can be polled using tokio::select!. This means that even while waiting for a key press, we
will still send a Event::Tick and Event::Render at regular intervals.
We made a number of changes to the Tui struct.
We added a Deref and DerefMut so we can call tui.draw(|f| ...) to have it call
We moved the startup() and shutdown() functionality into the Tui struct.
We also added a CancellationToken so that we can start and stop the tokio task more easily.
We added Event variants for Resize, Focus, and Paste.
We added methods to set the tick_rate, frame_rate, and whether we want to enable mouse or
Here’s the code for the fully async application:
The above code ensures that we render at a consistent frame rate. As an exercise, play around with
this frame rate and tick rate to see how the CPU utilization changes as you change those numbers.
Even though our application renders in an “async” manner, we also want to perform “actions” in an
asynchronous manner. We will improve this in the next section to make our application truly async