Previously, in the multiple file version of the counter app, in
event.rs we created an EventHandler using
std::thread::spawn, i.e. OS threads.
In this section, we are going to do the same thing with “green” threads or tasks, i.e. rust’s
async-await features + a future executor. We will be using tokio for this.
Here’s example code of reading key presses asynchronously comparing std::thread and tokio::task.
Notably, we are using tokio::sync::mpsc channels instead of std::sync::mpsc channels. And
because of this, receiving on a channel needs to be .await‘d and hence needs to be in a async fn
Even with this change, our EventHandler behaves the same way as before. In order to take advantage
of using tokio we have to use tokio::select!.
We can use tokio’s select! macro to wait on multiple
async computations and return when a any single computation completes.
Here’s what the EventHandler looks like with the select! macro:
As mentioned before, since EventHandler::next() is a async function, when we use it we have to
call .await on it. And the function that is the call site of event_handler.next().await also
needs to be an async function. In our tutorial, we are going to use the event handler in the
run() function which will now be async.
Also, now that we are getting events asynchronously, we don’t need to call
crossterm::event::poll() in the update function. Let’s make the update function take an
If you place the above EventHandler in a src/tui.rs file, then here’s what our application now
Using tokio in this manner however only makes the key events asynchronous but doesn’t make the
rest of our application asynchronous yet. We will discuss that in the next section.