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();
}