Blocking Code
To run non async code / blocking in a Task
or a Subscription
we can use tokio::task::spawn_blocking
Note: This might only work on native and not on wasm
Example
Here is a small example that shows how to use tokio::task::spawn_blocking
.
Cargo.toml
Because we want to use spawn_blocking
from tokio we need to add the tokio
feature to iced. This will lead to iced using tokio.
[dependencies]
iced = { version = "0.13.1", features = ["tokio"] }
tokio = { version = "1.38.0", features = ["rt"] }
Actual Example
In the example, there will be a button and a text. A press onto the button will trigger a large computation to be started (in the example, we will just sleep a few seconds and return a number). If the computation finishes, the result will be shown in the text widget.
Our computation runs in a task, because we do not want to block our whole UI until it has finished.
Inside the task we call spawn_blocking
with a closure of our computation. To get the returned value of the closure, we need to await the JoinHandle
returned by spawn_blocking
.
That will give us the result of the heavy computation without blocking the UI.
#[derive(Debug, Clone)]
enum Message {
CalculatedInformation(i32),
StartCalculatingInformation,
}
#[derive(Default)]
struct App {
hard_to_process_information: Option<i32>,
calculation_in_progress: bool,
}
impl App {
fn update(&mut self, message: Message) -> iced::Task<Message> {
match message {
Message::CalculatedInformation(information) => {
// Set the information
self.hard_to_process_information = Some(information);
}
Message::StartCalculatingInformation => {
// Change the state to indicate that the calculation is in progress
self.calculation_in_progress = true;
// Return a task that will calculate the information
return iced::Task::future(async {
let information = tokio::task::spawn_blocking(|| {
println!("Calculating information...");
// Simulate a long computation
std::thread::sleep(std::time::Duration::from_secs(2));
// return some value
42
})
.await
.unwrap();
// Send the information back to the update function
Message::CalculatedInformation(information)
});
}
}
iced::Task::none()
}
fn view(&self) -> iced::Element<Message> {
iced::widget::column![
// Display the information if it is available
iced::widget::Text::new(self.hard_to_process_information.map_or(
"Information will be ready in a second...".to_string(),
|x| format!("Information: {}", x),
)),
// Display a button to start the calculation
iced::widget::button("Start Calculation").on_press_maybe(
if self.calculation_in_progress {
None
} else {
Some(Message::StartCalculatingInformation)
}
)
]
.into()
}
}
fn main() {
iced::run("Task Example", App::update, App::view).unwrap();
}
Full Code
#[derive(Debug, Clone)]
enum Message {
CalculatedInformation(i32),
StartCalculatingInformation,
}
#[derive(Default)]
struct App {
hard_to_process_information: Option<i32>,
calculation_in_progress: bool,
}
impl App {
fn update(&mut self, message: Message) -> iced::Task<Message> {
match message {
Message::CalculatedInformation(information) => {
// Set the information
self.hard_to_process_information = Some(information);
}
Message::StartCalculatingInformation => {
// Change the state to indicate that the calculation is in progress
self.calculation_in_progress = true;
// Return a task that will calculate the information
return iced::Task::future(async {
let information = tokio::task::spawn_blocking(|| {
println!("Calculating information...");
// Simulate a long computation
std::thread::sleep(std::time::Duration::from_secs(2));
// return some value
42
})
.await
.unwrap();
// Send the information back to the update function
Message::CalculatedInformation(information)
});
}
}
iced::Task::none()
}
fn view(&self) -> iced::Element<Message> {
iced::widget::column![
// Display the information if it is available
iced::widget::Text::new(self.hard_to_process_information.map_or(
"Information will be ready in a second...".to_string(),
|x| format!("Information: {}", x),
)),
// Display a button to start the calculation
iced::widget::button("Start Calculation").on_press_maybe(
if self.calculation_in_progress {
None
} else {
Some(Message::StartCalculatingInformation)
}
)
]
.into()
}
}
fn main() {
iced::run("Task Example", App::update, App::view).unwrap();
}