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

Rust-Wasm中调用setTimeout相关方法报错,求正确实现方式

Rust-Wasm中正确调用setTimeout的解决方案

问题场景

在WebAssembly开发贪吃蛇游戏时,调用window().set_timeout_with_callback_and_timeout_and_arguments_0触发如下错误:

Uncaught Error: expected a number argument, found undefined
    at _assertNum (index_bg.js:246:1)
    at __wbg_adapter_27 (index_bg.js:257:1)
    at real (index_bg.js:214:1)

对应的Rust游戏循环代码:

fn game_loop(snake: Rc<RefCell<Snake>>, canvas: Rc<Canvas>, time: u32){
        let cl = Closure::<dyn FnMut(_)>::new(move |_: i32|{
            game_loop(snake.clone(), canvas.clone(), time);
            snake.borrow_mut().update();
            snake.borrow().draw(&canvas);
        });
        
        let _ = utils::window().set_timeout_with_callback_and_timeout_and_arguments_0(cl.as_ref().unchecked_ref(), 100);
    }

    game_loop(snake, Rc::new(canvas), 100);

查看自动生成的JS适配器函数:

function __wbg_adapter_27(arg0, arg1, arg2) {
    _assertNum(arg0);
    _assertNum(arg1);
    _assertNum(arg2);
    wasm._dyn_core__ops__function__FnMut__A____Output___R_as_wasm_bindgen__closure__WasmClosure___describe__invoke__hcf9afe44e302a4ef(arg0, arg1, arg2);
}

问题核心:适配器函数要求arg2为数字,但实际传入值为undefined,尝试set_timeout_with_callback_and_timeout_and_arguments_1也未解决。


问题原因

你定义的闭包声明了接收i32参数,但set_timeout_with_callback_and_timeout_and_arguments_0对应的是不接收任何参数的回调。Wasm-bindgen会根据闭包签名生成适配逻辑,当带参数的闭包匹配无参数的setTimeout调用时,会出现参数数量不匹配——适配器期望3个数字参数,实际仅传入2个,导致第三个参数arg2为undefined。


正确实现方式

1. 无参数回调场景

如果不需要传递额外参数,调整闭包签名为无参数类型,匹配set_timeout_with_callback_and_timeout_and_arguments_0的要求:

fn game_loop(snake: Rc<RefCell<Snake>>, canvas: Rc<Canvas>, time: u32){
    let cl = Closure::<dyn FnMut()>::new(move || {
        game_loop(snake.clone(), canvas.clone(), time);
        snake.borrow_mut().update();
        snake.borrow().draw(&canvas);
    });
    
    // 调用无参数版本的setTimeout
    let _ = utils::window().set_timeout_with_callback_and_timeout_and_arguments_0(
        cl.as_ref().unchecked_ref(), 
        time
    );
    // 关键:调用forget()防止闭包被Rust内存回收销毁
    cl.forget();
}

2. 带参数回调场景

如果需要向回调传递参数,使用对应参数数量的setTimeout方法,同时保证闭包签名匹配参数类型:

fn game_loop(snake: Rc<RefCell<Snake>>, canvas: Rc<Canvas>, time: u32){
    let cl = Closure::<dyn FnMut(u32)>::new(move |timeout| {
        game_loop(snake.clone(), canvas.clone(), timeout);
        snake.borrow_mut().update();
        snake.borrow().draw(&canvas);
    });
    
    // 调用带1个参数的setTimeout方法
    let _ = utils::window().set_timeout_with_callback_and_timeout_and_arguments_1(
        cl.as_ref().unchecked_ref(), 
        time,
        time // 传入要传递给回调的参数
    );
    cl.forget();
}

核心注意点

  • 闭包的参数数量、类型必须与调用的setTimeout方法后缀数字(_0/_1等)严格匹配;
  • 必须调用cl.forget(),否则Rust的自动内存回收会销毁闭包,导致回调执行时出现错误;
  • 超时时间参数需确保为有效数字类型(如u32),避免类型不匹配问题。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.28 20:55:32