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

如何在Rust WASM应用中轮询Future?

如何在Rust WASM应用中轮询Future?

嘿,我刚好碰到过类似的场景——用reqwest在WebXR渲染循环里搞异步请求,不想阻塞渲染流程对吧?手动调用poll确实有点绕,我来给你拆解一下怎么弄,还有更省心的替代方案。

首先得搞明白:直接手动轮询Future不是最推荐的方式,但如果你的场景必须这么做,核心是要搞定Context里的Waker——这玩意儿负责在Future有进展的时候通知程序再来poll它。在WASM环境里,这个Waker得和浏览器的事件循环绑定,不然你的请求可能永远卡在Pending状态。

手动轮询的步骤(适合一定要自己控制时机的场景)

首先得引入必要的依赖,Cargo.toml里加上这些:

[dependencies]
reqwest = { version = "0.11", features = ["wasm-bindgen"] }
wasm-bindgen = "0.2"
wasm-bindgen-futures = "0.4"
futures = "0.3"
log = "0.4"  # 用来打日志看状态

然后写代码的思路是:把Future存起来(得用Pin<Box<...>>,因为Future poll的时候不能随便移动内存),每次渲染循环里拿出来poll一下,根据结果处理:

use futures::Future;
use std::pin::Pin;
use wasm_bindgen::prelude::*;
use reqwest::Client;

// 你的应用结构体,存pending的请求和其他渲染状态
#[wasm_bindgen]
pub struct XRApp {
    pending_request: Option<Pin<Box<dyn Future<Output = Result<reqwest::Response, reqwest::Error>>>>>,
    // 比如这里可以加WebXR的渲染上下文之类的
}

#[wasm_bindgen]
impl XRApp {
    pub fn new() -> Self {
        // 初始化reqwest客户端
        let client = Client::new();
        // 发起请求,得到一个Future
        let request_future = client.get("https://your-api-url.com/data").send();
        // 把Future装箱并Pin住,存到结构体里
        XRApp {
            pending_request: Some(Box::pin(request_future)),
        }
    }

    pub fn render_frame(&mut self) {
        // 检查有没有待处理的请求
        if let Some(mut future) = self.pending_request.take() {
            // 获取和浏览器事件循环绑定的Waker,这步很关键!
            let waker = wasm_bindgen_futures::queue::queue().waker();
            let mut ctx = std::task::Context::from_waker(&waker);

            // 开始轮询Future
            match future.as_mut().poll(&mut ctx) {
                std::task::Poll::Ready(result) => {
                    // 请求完成啦,处理结果
                    match result {
                        Ok(response) => {
                            log::info!("请求成功!状态码: {}", response.status());
                            // 这里可以把响应数据存起来,比如转成JSON存到结构体里,后面渲染用
                        }
                        Err(e) => {
                            log::error!("请求炸了: {}", e);
                        }
                    }
                    // 请求处理完,清空pending状态
                    self.pending_request = None;
                }
                std::task::Poll::Pending => {
                    // 请求还没好,把Future放回结构体,下次渲染再试
                    self.pending_request = Some(future);
                }
            }
        }

        // 这里该干嘛干嘛,正常渲染WebXR内容
        // ...
    }
}

这里要注意几个点:

  • Pin<Box<dyn Future>>:必须用这个,因为Future的poll方法要求它不能被移动,Pin就是用来固定它的内存位置的。
  • Waker:用wasm_bindgen_futures提供的queue获取的Waker,会把Future的唤醒事件绑定到浏览器的事件循环,这样当reqwest的请求有进展时,浏览器会自动安排下一次poll,不会让你一直空等。

更省心的替代方案(推荐)

其实手动轮询有点折腾,Rust WASM的生态已经帮我们做好了更简单的方式——用wasm_bindgen_futures::spawn_local把Future丢给浏览器的事件循环去处理,等请求完成后更新应用状态,渲染循环只需要读状态就行:

#[wasm_bindgen]
pub struct XRApp {
    fetched_data: Option<String>,
    // 其他渲染状态
}

#[wasm_bindgen]
impl XRApp {
    pub fn new() -> Self {
        let mut app = XRApp { fetched_data: None };
        let client = Client::new();

        // 把请求Future spawn到浏览器事件循环里
        wasm_bindgen_futures::spawn_local(async move {
            match client.get("https://your-api-url.com/data").send().await {
                Ok(response) => {
                    if let Ok(text) = response.text().await {
                        // 请求完成,更新应用状态
                        app.fetched_data = Some(text);
                    }
                }
                Err(e) => {
                    log::error!("请求失败: {}", e);
                }
            }
        });

        app
    }

    pub fn render_frame(&self) {
        // 渲染的时候检查有没有拿到数据,有的话就画出来
        if let Some(data) = &self.fetched_data {
            log::info!("要渲染的数据: {}", data);
            // 这里就可以把data转换成你要画的图形啦
        }

        // 正常渲染WebXR内容
        // ...
    }
}

这种方式不用管什么poll和Context,完全交给async生态处理,代码简洁多了,也不容易出错。

总的来说,如果不是特别需要精确控制poll的时机,优先用spawn_local的方式;如果必须自己在渲染循环里控制,就按照第一个方案来,核心是搞定Waker和Pin的问题。

备注:内容来源于stack exchange,提问作者Mutant Bob

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.04.13 17:08:14