【Tauri】Tauri中Channel的使用

12 阅读2分钟

Channel 在 Tauri 中是前端与 Rust 后端进行异步消息通信的桥梁,常用于事件流(如 SSE、WebSocket、长连接)场景。


作用

  • 前端 JS 通过 Channel 实例接收后端 Rust 主动推送的消息。
  • 适合用在“事件驱动”或“流式数据”场景,比如服务端事件(SSE)、实时日志、推送等。

一般用法

1. 前端创建 Channel 并传给 Rust

import { Channel, invoke } from '@tauri-apps/api/core';

const chan = new Channel();
chan.onmessage = (msg) => {
  // 这里处理 Rust 主动推送的消息
  console.log('收到消息:', msg);
};

const res = await invoke('your_rust_command', { chan }); // 划重点:res何时被打印??

2. Rust 端接收 Channel 并发送消息

#[tauri::command]
async fn your_rust_command(chan: tauri::ipc::Channel) {
    // 发送消息到前端
    chan.send("hello from rust!").unwrap();
    // 或循环推送事件
    loop {
        chan.send("实时数据").unwrap();
        tokio::time::sleep(Duration::from_secs(1)).await;
    }
}

问题一:channel 什么时候结束?

  • 只要不调用 destroy_client(即不关闭/移除该 SSE 客户端),Rust 端的 sse_open_stream 里的 loop 就会一直运行,chan.send(...) 会不断推送消息到前端,channel 不会结束

  • 只有当:

    • 收到 rx.recv()(即 destroy_client 被调用,或 sse_close_stream 被前端 invoke),或者
    • SSE 流断开/出错/服务端关闭 才会 break 退出循环,channel 才会结束。
#[tauri::command]
pub async fn sse_close_stream(url: &str) -> Result<(), String> {
    destroy_client(url.to_string());
    Ok(())
}

问题2: 是不是只要连接一直在res就一直打印不出来,只有断了连接才会走后面的打印?

是的

  • 只要 Rust 端的 sse_open_stream 没有 break(即 SSE 连接还在),await invoke(...) 就会一直 pending,不会执行到 console.log(res)
  • 只有当 SSE 连接断开、出错,或者你主动调用 destroy_client,Rust 端的循环 break,函数返回,res 才会有值,console.log(res) 才会执行。

实例

下面代码中有四个debugger,尝试猜测一下进断点先后顺序

    const startSSE = async () => {
        const req_url = `${getRcsApiPrefix()}/sse/connect?clientKey=${clientKey}`;
        try {
            let flag = true;
            const chan = new Channel();
            chan.onmessage = async (eve) => {
                debugger // 1
                if (!flag) return;
                const data = JSON.parse(eve);
                if (callback && !callback(data)) {
                    flag = false;
                    await invoke("sse_close_stream", { url: req_url });
                }
            };
            debugger //2
            const res = await invoke("sse_open_stream", { url: req_url, chan });
            console.log(res)
            debugger // 3
            if (res === 1 || (res === 2 && !flag)) {
                timer = setTimeout(() => { startSSE(); }, 25);
            } 
        } catch (error) {
            console.log(`SSE请求失败,尝试重连: ${error}, 时刻: ${new Date()}`);
            timer = setTimeout(() => { startSSE(); }, 3000);
        }
    }
    const closeSSE = async () => {
        debugger // 4
        const req_url = `${getRcsApiPrefix()}/sse/connect?clientKey=${clientKey}`;
        await invoke("sse_close_stream",{url: req_url});

    }

    onMounted(async () => {
        startSSE();
    });
    onUnmounted(async () => {
        sseConnections.clear();
        await closeSSE();
        clearTimeout(timer);
    });

答案:2143

  • 初始化时:先建立Channel,后请求sse_open_stream,先进2;
  • 建立连接后开始实时通信,进入chan.onmessage回调,进入1;
  • 一直通信,await invoke(...) 一直 pending;
  • 离开界面,调用closeSSE(),进入4;
  • 连接销毁,await invoke()结束,res被赋值,进入3。

总结

  • Channel 是 Tauri 前后端异步消息推送的桥梁
  • 前端用 chan.onmessage 监听消息,Rust 端用 chan.send() 主动推送。
  • 适合流式、事件驱动的数据通信。
  • channel 只要不 destroy_client 就不会结束,invoke 也不会返回。
  • 只有连接断开或 destroy_client 后,invoke 才会返回,后续代码才会执行。