惯性聚合 高效追踪和阅读你感兴趣的博客、新闻、科技资讯
阅读原文 在惯性聚合中打开

推荐订阅源

博客园 - 【当耐特】
小众软件
小众软件
S
SegmentFault 最新的问题
GbyAI
GbyAI
量子位
爱范儿
爱范儿
L
LangChain Blog
Vercel News
Vercel News
A
About on SuperTechFans
腾讯CDC
博客园_首页
酷 壳 – CoolShell
酷 壳 – CoolShell
月光博客
月光博客
博客园 - 聂微东
Stack Overflow Blog
Stack Overflow Blog
H
Help Net Security
U
Unit 42
奇客Solidot–传递最新科技情报
奇客Solidot–传递最新科技情报
V
V2EX
V
Visual Studio Blog
美团技术团队
D
DataBreaches.Net
The GitHub Blog
The GitHub Blog
N
Netflix TechBlog - Medium

DEV Community

Authentication Security Deep Dive: From Brute Force to Salted Hashing (With Java Examples) Why AI Systems Don’t Fail — They Drift Spilling beans for how i learn for exam😁"Reinforcement Learning Cheat Sheet" I Replaced Chrome with Safari for AI Browser Automation. Here's What Broke (and What Finally Worked) How Python Borrows Other People's Work The $40 Architecture: Processing 1 Billion API Requests with 99.99% Uptime Vibe Coding: A Workflow Guide (From Zero to SaaS) Most webhook security guides protect the wrong side. The scary part is delivery. Headless CMS for TanStack Start: Build a Blog with Cosmic EU Age Verification App "Hacked in 2 Minutes" — What Actually Happened Comfy Cloud’s delete function does not actually remove files Running AI Models on GPU Cloud Servers: A Beginner Guide Event-driven media intelligence with AWS Step Functions and Bedrock I scored 500 AI prompts across 8 quality dimensions — here's what broke How to Call Google Gemini API from Next.js (Free Tier, No Backend Needed) The Portal Protocol: Reclaiming Human Connection in the Age of AI How to Fix Your Team's Scattered Knowledge Problem With a Self-Hosted Forum Intro to tc Cloud Functors: A Graph-First Mental Model for the Modern Cloud Designing Multi-Tenant Backends With Both Ownership and Team Access I Built a Neumorphic CSS Library with 77+ Components — Here's What I Learned PostgreSQL Performance Optimization: Why Connection Pooling Is Critical at Scale Cómo construí un SaaS multi-rubro para gestionar expensas en Argentina con FastAPI + Vue 3 🚀 I Built an Ethical Hacking Scanner Tool – Open Source Project I Replaced /usage and /context in Claude Code With a Single Statusline A Pythonic Way to Handle Emails (IMAP/SMTP) with Auto-Discovery and AI-Ready Design I Collected 8.9 Million Polymarket Price Points — Here's What I Found About How Markets Really Move EcoTrack AI — Carbon Footprint Tracker & Dashboard Everyone's Using AI. No One Agrees How. 5 self-hosted ebook managers worth trying in 2026 Building Your First AI Agent with LangChain: From Chatbot to Autonomous Assistant
Rust Async Patterns in Tauri — Keeping the UI Responsive ...
hiyoyo · 2026-05-06 · via DEV Community
Cover image for Rust Async Patterns in Tauri — Keeping the UI Responsive While Rust Does Heavy Work

hiyoyo

If this is useful, a ❤️ helps others find it.

All tests run on an 8-year-old MacBook Air.

A Tauri app has two threads that matter: the main thread (UI) and whatever tokio spawns. Block the main thread and the UI freezes. Block for too long in a command and the frontend times out.

Here's how I keep things responsive in practice.


The basic rule

Never do blocking work in a #[tauri::command] without async.

// Bad — blocks the thread pool
#[tauri::command]
pub fn compress_pdf(path: String) -> Result<(), String> {
    heavy_compression_work(&path)?;  // takes 3 seconds, blocks
    Ok(())
}

// Good — async, non-blocking
#[tauri::command]
pub async fn compress_pdf(path: String) -> Result<(), String> {
    tokio::task::spawn_blocking(move || {
        heavy_compression_work(&path)
    })
    .await
    .map_err(|e| e.to_string())?
    .map_err(|e| e.to_string())
}

Enter fullscreen mode Exit fullscreen mode

spawn_blocking moves CPU-heavy work to a dedicated thread pool, freeing the async executor for other tasks.


Progress reporting during long operations

For operations that take more than a second, report progress via events:

#[tauri::command]
pub async fn batch_process(
    paths: Vec,
    window: tauri::Window,
) -> Result<(), String> {
    let total = paths.len();

    for (i, path) in paths.iter().enumerate() {
        process_single(&path).await?;

        window.emit("batch-progress", serde_json::json!({
            "current": i + 1,
            "total": total,
            "percent": ((i + 1) as f64 / total as f64 * 100.0) as u32,
        })).ok();
    }

    Ok(())
}

Enter fullscreen mode Exit fullscreen mode

// Frontend shows live progress
await listen('batch-progress', (event) => {
  setProgress(event.payload.percent);
});

await invoke('batch_process', { paths });

Enter fullscreen mode Exit fullscreen mode


Cancellation

Users cancel long operations. Support it with a shared flag:

use std::sync::{Arc, atomic::{AtomicBool, Ordering}};

pub struct CancelToken(Arc);

impl CancelToken {
    pub fn new() -> Self { Self(Arc::new(AtomicBool::new(false))) }
    pub fn cancel(&self) { self.0.store(true, Ordering::Relaxed); }
    pub fn is_cancelled(&self) -> bool { self.0.load(Ordering::Relaxed) }
}

#[tauri::command]
pub async fn batch_process(
    paths: Vec,
    cancel_token: tauri::State<'_, CancelToken>,
    window: tauri::Window,
) -> Result<(), String> {
    for (i, path) in paths.iter().enumerate() {
        if cancel_token.is_cancelled() {
            return Err("cancelled".to_string());
        }
        process_single(&path).await?;
        window.emit("batch-progress", i + 1).ok();
    }
    Ok(())
}

Enter fullscreen mode Exit fullscreen mode

// Cancel button
await invoke('cancel_batch');

Enter fullscreen mode Exit fullscreen mode


Parallel processing with Semaphore

Process multiple files concurrently, but not all at once:

use tokio::sync::Semaphore;
use std::sync::Arc;

pub async fn process_parallel(paths: Vec) -> Vec> {
    let semaphore = Arc::new(Semaphore::new(4)); // max 4 concurrent
    let mut handles = Vec::new();

    for path in paths {
        let sem = semaphore.clone();
        let handle = tokio::spawn(async move {
            let _permit = sem.acquire().await.unwrap();
            process_single(&path).await.map_err(|e| e.to_string())
        });
        handles.push(handle);
    }

    futures::future::join_all(handles)
        .await
        .into_iter()
        .map(|r| r.unwrap_or_else(|e| Err(e.to_string())))
        .collect()
}

Enter fullscreen mode Exit fullscreen mode

4 concurrent transfers on a 2017 MacBook Air runs well without saturating the disk or CPU.


Hiyoko PDF Vault → https://hiyokoko.gumroad.com/l/HiyokoPDFVault
X → @hiyoyok