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
相关产品推荐
相关产品推荐

