如何从Wasm向JavaScript返回含非Copy/Clone类型的元组?
实现方案
要解决Wasm中返回futures通道收发器的问题,我们可以分别包装Sender和Receiver,通过暴露异步方法让JavaScript侧正常调用,同时保证单一所有权需求,具体实现如下:
1. 依赖准备
首先在Cargo.toml中添加所需依赖:
[dependencies] wasm-bindgen = "0.2" futures = "0.3" wasm-bindgen-futures = "0.4" js-sys = "0.3"
2. 包装收发器并暴露方法
use futures::{channel::mpsc, stream::Stream, StreamExt}; use wasm_bindgen::prelude::*; use wasm_bindgen_futures::JsFuture; use js_sys::Promise; #[wasm_bindgen] pub struct WrappedSender { sender: mpsc::Sender<i32>, } #[wasm_bindgen] impl WrappedSender { // 暴露发送方法,返回Promise表示发送结果 pub fn send(&mut self, value: i32) -> Promise { let sender = self.sender.clone(); JsFuture::from(async move { sender.send(value).await .map_err(|e| JsValue::from_str(&format!("发送失败: {:?}", e)))?; Ok(JsValue::UNDEFINED) }).into() } } #[wasm_bindgen] pub struct WrappedReceiver { receiver: Pin<Box<dyn Stream<Item = i32> + 'static>>, } #[wasm_bindgen] impl WrappedReceiver { // 暴露获取下一个元素的方法,返回Promise,元素存在时返回数值,流结束返回null pub fn next(&mut self) -> Promise { let mut receiver = self.receiver.as_mut(); JsFuture::from(async move { match receiver.next().await { Some(val) => Ok(JsValue::from(val)), None => Ok(JsValue::NULL), } }).into() } } #[wasm_bindgen] pub fn channel() -> (WrappedSender, WrappedReceiver) { let (tx, rx) = mpsc::channel(32); ( WrappedSender { sender: tx }, WrappedReceiver { receiver: Box::pin(rx) }, ) }
3. 关键细节说明
- 所有权保证:
WrappedReceiver内部持有Pin<Box<dyn Stream>>,通过&mut self调用next方法,确保对接收流的独占可变访问,无需Rc<RefCell>这类多所有权包装,符合单一所有权需求。 - JavaScript兼容性:返回的元组可直接在JS中解构,例如:
import { channel } from './pkg/your_wasm_module.js'; async function useChannel() { const [sender, receiver] = channel(); // 发送数据 await sender.send(42); // 接收数据 const val = await receiver.next(); console.log(val); // 输出42 } - 异步适配:借助
wasm_bindgen_futures将Rust的Future转换为JS的Promise,让两端异步逻辑无缝对接。
内容的提问来源于stack exchange,提问作者Kevin
相关产品推荐
相关产品推荐

