Making async network calls in the background in a GUI application

Let's assume that the GUI program runs in the main thread. Let's say I now want to make several async network calls for data synchronization purposes. I want to do it on a background thread (or process) so that the GUI can function normally. and doesn't wait on the network io.

When this background task is completed I want it to be able to send some kind of notification to the GUI that it is done, so the GUI can refresh itself and make use of the newly available data.

I was reading up on how to do this and came across two approaches described here: Bridging with sync code | Tokio - An asynchronous Rust runtime

  1. Spawning things on a runtime: The Runtime object has a method called spawn. When you call this method, you create a new background task to run on the runtime.

  2. Sending messages: The third technique is to spawn a runtime and use message passing to communicate with it.

I am not actually sure I understand the difference between the two approaches. Especially since under the spawning things on a runtime they also say

The example waits for the spawned tasks to finish by calling block_on on the JoinHandle returned by the call to spawn, but this isn't the only way to do it. Here are some alternatives:

  • Use a message passing channel such as tokio::sync::mpsc.
  • Modify a shared value protected by e.g. a Mutex. This can be a good approach for a progress bar in a GUI, where the GUI reads the shared value every frame.

Can someone explain what is the best way to accomplish what I want?

You show no need to use async. Spawning a thread for the network call seems all that is needed.
Check your GUI documentation on how to send it messages to it, for when done.

The API Client library I am using is async by default and does not have a blocking version of the Client. What do I do then?

Also it's not a single network call. I need to hit 3 endpoints

/api/endpoint1
/api/endpoint2
/api/endpoint3

And each endpoint is a paginated API, so I need to make multiple API calls to each so that I can download all the data. Would async make that faster?

The way to do this will depend on the GUI framework you are using. GUI frameworks that require all GUI manipulation to be on a single main thread will generally have some means of sending a closure or message to be executed or delivered on the main thread.

What GUI framework are you using?

I am using Iced.

I'm not previously familiar with iced, but it has an example of a download progress bar. You should be able to adapt that to your needs.

It seems that iced::subscription::unfold is how you call external async code and produce iced messages that can update your GUI. I don't know what thread it runs that on, but if it's the main thread, then you just need to spawn your download task and have the async fn provided to unfold be one that awaits the spawned task's join handle before producing a message that it's done.

Maybe there's an even better way (designed for single events and not streams), but this one will surely work.

1 Like