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

使用deno_core op2异步函数时如何访问OpState及Tauri通信实践

在Tauri+deno_core异步op中正确访问OpState的方案

问题背景

开发基于Tauri+deno_core的应用,需要通过op实现插件间通信的emit/listen方法,其中listen为异步op,但访问OpState时遇到两类错误:

使用Rc<OpState>作为参数时的错误

error: custom attribute panicked
   --> src/deno.rs:391:1
    |
391 | #[op2(async)]
    | ^^^^^^^^^^^^^
    |
    = help: message: Failed to parse #[op2]:
             - Failed to map signature to V8
             - Unable to map argument Rc(OpState) to a slow argument

使用&mut OpState直接访问时的生命周期错误

error: lifetime may not live long enough
   --> src/deno.rs:373:1
    |
373 | #[op2(async)]
    | ^^^^^^^^^^^^^
    | |
    | lifetime `'s` defined here
    | type annotation requires that `'s` must outlive `'static`
    |
    = note: this error originates in the attribute macro `op2` (in Nightly builds, run with -Z macro-backtrace for more info)

error[E0716]: temporary value dropped while borrowed
   --> src/deno.rs:373:1
    |
373 | #[op2(async)]
    | ^^^^^^^^^^^^-
    | |           |
    | |           temporary value is freed at the end of this statement
    | creates a temporary value which is freed while still in use
    | argument requires that borrow lasts for `'static`
    |
    = note: this error originates in the attribute macro `op2` (in Nightly builds, run with -Z macro-backtrace for more info)

解决方案

1. 正确获取OpState实例

不要直接将OpState作为参数传入异步op,而是通过OpState::get()方法从AppHandle中获取:

use deno_core::OpState;
use tauri::AppHandle;

#[op2(async)]
pub async fn listen(app_handle: AppHandle, event_name: String) -> Result<(), ()> {
    // 从AppHandle中获取OpState
    let op_state = OpState::get(&app_handle).expect("Failed to get OpState");
    
    // 借用OpState中的自定义状态
    let custom_state = op_state.borrow::<YourCustomState>();
    
    // 执行监听器注册逻辑
    Ok(())
}

2. 异步场景的生命周期安全处理

如果需要在异步任务中持续访问状态,用Rc<RefCell<...>>包装自定义状态,从OpState中克隆Rc后使用,避免生命周期冲突:

use std::collections::HashMap;
use std::rc::Rc;
use std::cell::RefCell;
use deno_core::OpState;
use tauri::AppHandle;

// 定义带线程安全包装的自定义状态
#[derive(Clone)]
struct EventState {
    listeners: Rc<RefCell<HashMap<String, Vec<Box<dyn Fn(String) + Send + Sync>>>>>,
}

// 初始化时将状态存入OpState
pub fn init_op_state(state: &mut OpState) {
    state.put(EventState {
        listeners: Rc::new(RefCell::new(HashMap::new())),
    });
}

#[op2(async)]
pub async fn listen(app_handle: AppHandle, event_name: String) -> Result<(), ()> {
    let op_state = OpState::get(&app_handle).unwrap();
    // 克隆状态的Rc,以便在异步任务中持有
    let event_state = op_state.borrow::<EventState>().clone();
    
    // 在异步任务中安全访问状态
    tokio::spawn(async move {
        let mut listeners = event_state.listeners.borrow_mut();
        // 注册新的监听器逻辑
        listeners.entry(event_name).or_default().push(Box::new(|msg| {
            // 处理事件消息
        }));
    });
    
    Ok(())
}

3. Tauri+deno_core通信最佳实践

  • 优先用Tauri内置事件系统:如果插件间通信不需要直接操作V8上下文,直接用Tauri的app.emit_all()和app.listen(),无需涉及OpState,更简洁。
  • OpState仅存JS相关状态:将业务逻辑状态放在Tauri的全局状态或AppHandle扩展中,OpState只存放和deno_core JS环境交互的必要数据。
  • 避免长期持有OpState引用:异步op中尽量快速完成OpState的访问,需要长期操作时用克隆后的状态副本,防止生命周期错误。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.15 03:40:07