フロントエンドからのリクエストに対して、Rust 側で時間のかかる処理を行う場合、UI をフリーズさせないために非同期コマンド (async fn) として実装し、適切なスレッド戦略を取る必要があります。
1. アンチパターン (UIフリーズの原因)
以下のコードは非同期関数ですが、重い処理が Tokio のワーカースレッドを占有してしまうため、他のイベント処理が遅延する可能性があります。
// 良くない例: CPUバウンドな処理を直接実行
#[tauri::command]
async fn heavy_task() {
let mut sum = 0;
for i in 0..10_000_000_000 { // 長いループ
sum += i;
}
}
2. 推奨パターン: spawn_blocking
CPU バウンドな処理(計算、画像処理、暗号化など)は spawn_blocking で専用のスレッドプールに逃がします。
#[tauri::command]
async fn heavy_task_optimized() -> Result<u64, String> {
let result = tauri::async_runtime::spawn_blocking(move || {
// 重い処理をここで行う
let mut sum: u64 = 0;
for i in 0..1_000_000_000 {
sum += i;
}
sum
})
.await
.map_err(|e| e.to_string())?;
Ok(result)
}
3. 進捗状況の通知 (Progress Reporting)
処理時間が長い場合、ユーザーに進捗を伝えることが UX 上重要です。Window または AppHandle を使用してイベントを発行します。
use tauri::Emitter;
#[tauri::command]
async fn task_with_progress(window: tauri::Window) -> Result<(), String> {
tauri::async_runtime::spawn_blocking(move || {
for i in 0..100 {
// 処理...
std::thread::sleep(std::time::Duration::from_millis(50));
// 進捗イベント発行
let _ = window.emit("progress", i);
}
}).await.map_err(|e| e.to_string())?;
Ok(())
}