JavaScript 前端通过 commands 与后端通信。
Command 是一个字符串消息,通过 invoke 和 promisified API 调用发送到后端, 后端可以通过 invoke_handler 回调函数来监听。
Tauri 默认将每条消息序列化为一个 JSON 字符串,所以你可以在后端使用serde_json来反序列化它。
同步命令
使用 invoke API 将一个同步命令发送到后端:
// with a module bundler (recommended): import { invoke } from ‘tauri/api/tauri’ // with vanilla JS: var invoke = window.TAURI.tauri.invoke // then call it: invoke({ cmd: ‘doSomething’, count: 5, payload: { state: ‘some string data’, data: 17 } })
若要在 Rust 上读取消息,请使用 invoke_handler:
use serde::Deserialize; #[derive(Deserialize)] struct DoSomethingPayload { state: String, data: u64 } // The commands definitions // Deserialized from JS #[derive(Deserialize)] #[serde(tag = “cmd”, rename_all = “camelCase”)] enum Cmd { DoSomething { count: u64, payload: DoSomethingPayload, } } fn main() { tauri::AppBuilder::new() .invoke_handler(|_webview, arg| { use Cmd::*; match serde_json::from_str(arg) { Err(e) => Err(e.to_string()), Ok(command) => { match command { DoSomething { count, payload } => { // do some synchronous operation with count
and payload
sent from the frontend // note that this blocks the UI thread, so prefer asynchronous commands for long-running tasks } } Ok(()) } } }) .build() .run(); }
异步命令
使用 promisified API 将一个异步命令发送到后端:
// with a module bundler (recommended): import { promisified } from ‘tauri/api/tauri’ // with vanilla JS: var promisified = window.TAURI.tauri.promisified // then call it: promisified({ cmd: ‘doSomething’, count: 5, payload: { state: ‘some string data’, data: 17 } }).then(response => { // do something with the Ok() response const { value, message } = response }).catch(error => { // do something with the Err() response string })
要在 Rust 上读取消息,请使用 invoke_handler 和 tauri::execute_promise Api。
用这种命令执行的代码将在一个单独的线程上执行。
要在主线程上运行代码,请使用 tauri::execute_promise_sync
注意两个附加属性: invoke 和 error。
- invoke 参数是Promise resolve 函数的名称。
- error 参数是Promise reject 函数的名称。
从 tauri::executepromise 函数返回的Result被用来确定 Promise 是否应该resolve 或 reject。
如果它是一个 Ok 变体,我们使用序列化为 JSON 的值来 resolve 这个 Promise。
否则,我们使用产生的错误字符串 reject 它。
use serde::{Deserialize, Serialize}; #[derive(Deserialize)] struct DoSomethingPayload { state: String, data: u64, } // The commands definitions // Deserialized from JS #[derive(Deserialize)] #[serde(tag = “cmd”, rename_all = “camelCase”)] enum Cmd { DoSomething { count: u64, payload: DoSomethingPayload, callback: String, error: String, }, } #[derive(Serialize)] struct Response<’a> { value: u64, message: &’a str, } // An error type we define // We could also use the anyhow
lib here #[derive(Debug, Clone)] struct CommandError<’a> { message: &’a str, } impl<’a> CommandError<’a> { fn new(message: &’a str) -> Self { Self { message } } } impl<’a> std::fmt::Display for CommandError<’a> { fn fmt(&self, f: &mut std::fmt::Formatter<’>) -> std::fmt::Result { write!(f, “{}”, self.message) } } // Tauri uses the anyhow
lib so custom error types must implement std::error::Error // and the function call should call .into()
on it impl<’a> std::error::Error for CommandError<’a> {} fn main() { tauri::AppBuilder::new() .invoke_handler(|_webview, arg| { use Cmd::*; match serde_json::from_str(arg) { Err(e) => Err(e.to_string()), Ok(command) => { match command { DoSomething { count, payload, callback, error } => tauri::execute_promise( _webview, move || { if count > 5 { let response = Response { value: 5, message: “async response!”, }; Ok(response) } else { Err(CommandError::new(“count should be > 5”).into()) } }, callback, error, ), } Ok(()) } } }) .build() .run(); }