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

在Yew函数组件的WASM环境中如何运行impl Future类型异步任务?

在Yew函数组件中运行异步HTTP请求任务的正确方式

问题场景

用户提供了一段Yew函数组件代码,尝试在按钮点击时发起异步HTTP请求,但无法正确运行异步任务。代码如下:

#[function_component(AuthSignUp)]
pub fn render_auth_sign_up() -> Html {

// .......

let onclick = Callback::from(move |_| {
    let username = (*username_clone).clone();
    let password = (*password_clone).clone();
    let user_reg = common::schemas::UserRegister {
        username,
        password, 
        email: "yo".to_string()
    };
    let body = serde_json::to_string(&user_reg).unwrap();
    let res = http::Request::get("api/auth/signin")
        .body(body)
        .unwrap()
        .send();
});

html!(
    <div class="center auth main-containers" id="auth-container">
        <div class="center" style="margin-bottom: 0.5em;">{"enter sign in details"}</div>
        <MyInput value={username} r#type="text" class="input center" id="input-username" placeholder="username"/>
        <MyInput value={password} r#type="password" class="input center" id="input-password" placeholder="password"/>
        <button {onclick}>{"done"}</button>
    </div>
)
};

其中res的类型为impl Future<Output = Result<Response, Error>>,用户尝试过wasm_bindgen_futures::spawn_local()和wasm_bindgen_futures::future_to_promise()但未成功。

解决方案

1. 修正HTTP请求方法错误

GET请求不应该携带请求体,注册/登录类接口通常使用POST方法,需将Request::get改为Request::post,同时确保接口路径与业务匹配(比如注册接口应为api/auth/signup而非signin)。

2. 用spawn_local正确处理异步任务

spawn_local可以在Wasm环境启动异步任务,虽然它要求任务无返回值,但我们可以在任务内部awaitFuture并处理结果,结合Yew的use_state管理组件状态,实现UI的实时更新。

修改后的完整代码示例

use yew::prelude::*;
use wasm_bindgen_futures::spawn_local;
use serde::Serialize;
use reqwest;

#[derive(Serialize)]
struct UserRegister {
    username: String,
    password: String,
    email: String,
}

#[function_component(AuthSignUp)]
pub fn render_auth_sign_up() -> Html {
    // 用use_state管理组件状态:加载状态、错误信息、输入值
    let (loading, set_loading) = use_state(|| false);
    let (error, set_error) = use_state(|| None::<String>);
    let (username, set_username) = use_state(String::new);
    let (password, set_password) = use_state(String::new);

    let onclick = {
        let username = username.clone();
        let password = password.clone();
        let set_loading = set_loading.clone();
        let set_error = set_error.clone();

        Callback::from(move |_| {
            set_loading.set(true);
            set_error.set(None);

            let user_reg = UserRegister {
                username: (*username).clone(),
                password: (*password).clone(),
                email: "yo".to_string(),
            };

            // 用spawn_local包裹异步任务
            spawn_local(async move {
                let result = reqwest::Client::new()
                    .post("api/auth/signup")
                    .json(&user_reg)
                    .send()
                    .await;

                match result {
                    Ok(response) => {
                        if response.status().is_success() {
                            // 处理成功逻辑:比如跳转首页、提示成功
                            log::info!("注册成功");
                        } else {
                            let err_msg = response.text().await.unwrap_or("请求失败".to_string());
                            set_error.set(Some(err_msg));
                        }
                    }
                    Err(e) => {
                        set_error.set(Some(e.to_string()));
                    }
                }

                set_loading.set(false);
            });
        })
    };

    html! {
        <div class="center auth main-containers" id="auth-container">
            <div class="center" style="margin-bottom: 0.5em;">{"enter sign up details"}</div>
            <input 
                type="text" 
                class="input center" 
                id="input-username" 
                placeholder="username"
                value={(*username).clone()}
                oninput={Callback::from(move |e: InputEvent| {
                    set_username.set(e.target_unchecked_into::<web_sys::HtmlInputElement>().value());
                })}
            />
            <input 
                type="password" 
                class="input center" 
                id="input-password" 
                placeholder="password"
                value={(*password).clone()}
                oninput={Callback::from(move |e: InputEvent| {
                    set_password.set(e.target_unchecked_into::<web_sys::HtmlInputElement>().value());
                })}
            />
            <button {onclick} disabled={*loading}>
                { if *loading { "loading..." } else { "done" } }
            </button>
            // 显示错误提示
            if let Some(err) = &*error {
                <div style="color: red; margin-top: 0.5em;">{err}</div>
            }
        </div>
    }
}

关键说明

  • spawn_local是Wasm环境中运行异步任务的标准方式,任务内部可以正常awaitFuture并处理结果。
  • 通过use_state管理输入值、加载状态和错误信息,确保UI能实时响应状态变化。
  • 修正HTTP方法后,服务器才能正确解析请求体,避免无效请求。
  • 在异步任务中处理请求结果,通过更新状态将反馈传递给用户,提升交互体验。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.17 22:15:36