You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Rust Tauri应用定时Reqwest请求阻塞主代码问题求助

解决Tauri应用定时请求阻塞主程序的问题

问题根源

直接在Tauri的setup函数中使用阻塞式的loop + thread::sleep()会卡住主线程——因为setup是同步执行的,主线程被阻塞后,Tauri的事件循环无法正常运行,导致整个应用停止响应。

解决方案步骤

1. 重构set_ip异步函数

移除set_ip函数上的#[tokio::main]宏,因为Tauri已经集成了Tokio runtime,不需要再创建新的runtime实例,直接保留普通异步函数即可:

// firebase.rs 修改后代码
pub async fn set_ip(ip: std::net::IpAddr, config: &tauri::Config) {
    let client = Client::new();

    let user = config::read(config).unwrap().user_data;

    let token = auth(user.email.as_str(), user.password.as_str()).await.unwrap();

    let response = client
        .get(
            format!(
                "https://firestore.googleapis.com/v1/my_db/(default)/documents/users/{}",
                user.email
            )
        )
        .header("Authorization", format!("Bearer {}", token))
        .header("content-type", "application/json")
        .send().await
        .unwrap()
        .text().await
        .unwrap();

    let mut data: serde_json::Value = serde_json::from_str(&response).expect("ERROR parsing");

    data["fields"]["ip"]["stringValue"] = serde_json::Value::String(ip.to_string());

    let _post_ip = client
        .patch(
            format!(
                "https://firestore.googleapis.com/v1/projects/my_db/documents/users/{}",
                user.email
            )
        )
        .body(serde_json::to_string(&data).expect("Error stringing value"))
        .header("Authorization", format!("Bearer {}", token))
        .header("content-type", "application/json")
        .send().await
        .unwrap();
}

2. 在setup中启动异步后台定时任务

使用Tokio的spawn将定时任务放到后台线程池执行,搭配tokio::time::Interval实现非阻塞的定时逻辑,避免阻塞主线程:

// main.rs 修改后setup代码
.setup(|app| {
    let config = app::storage::config::read(&app.config()).unwrap();

    if config.show_window_on_start {
        app::window::new(app.handle());
    }

    // 克隆配置,满足后台任务的'static生命周期要求
    let cloned_config = app.config().clone();
    // 启动异步后台任务
    tokio::spawn(async move {
        // 初始化30分钟间隔的定时器
        let mut interval = tokio::time::interval(tokio::time::Duration::from_secs(30 * 60));
        
        // 程序启动时先执行一次IP上报
        let ip = server::network::get_ip();
        if let Err(e) = app::api::firebase::set_ip(ip, &cloned_config).await {
            eprintln!("IP上报失败: {}", e);
        }

        // 进入定时循环
        loop {
            // 等待下一个时间点(非阻塞)
            interval.tick().await;
            let ip = server::network::get_ip();
            if let Err(e) = app::api::firebase::set_ip(ip, &cloned_config).await {
                eprintln!("IP上报失败: {}", e);
            }
        }
    });

    Ok(())
})

关键说明

  • tokio::spawn:将任务提交到Tokio线程池异步执行,不会阻塞主线程,保证Tauri的事件循环正常运行。
  • tokio::time::Interval:异步定时器,比阻塞式的thread::sleep()更适合异步场景,不会占用线程资源。
  • 错误处理:替换原代码中的unwrap()为if let Err,避免后台任务panic导致整个应用崩溃。

内容的提问来源于stack exchange,提问作者vstanika

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.22 06:10:31