mirror of
https://github.com/leptos-rs/leptos
synced 2024-11-10 06:44:17 +00:00
fix: don't leak canceled timeouts (#2331)
Instead of using `Closure::once_into_js`, this uses `into_js_value`, which uses weak references to clean up the closure when Javascript no longer has need of it. It would be nice to make this (and the similar interval function) drop the callback promptly when cancelled, but I don't think that's possible while keeping the handles Copy. Fixes #2330 Co-authored-by: Robert Macomber <robertm@mox>
This commit is contained in:
parent
aa3700ffb9
commit
47abe00993
1 changed files with 21 additions and 2 deletions
|
@ -103,6 +103,25 @@ pub fn request_animation_frame(cb: impl FnOnce() + 'static) {
|
|||
_ = request_animation_frame_with_handle(cb);
|
||||
}
|
||||
|
||||
// Closure::once_into_js only frees the callback when it's actually
|
||||
// called, so this instead uses into_js_value, which can be freed by
|
||||
// the host JS engine's GC if it supports weak references (which all
|
||||
// modern brower engines do). The way this works is that the provided
|
||||
// callback's captured data is dropped immediately after being called,
|
||||
// as before, but it leaves behind a small stub closure rust-side that
|
||||
// will be freed "eventually" by the JS GC. If the function is never
|
||||
// called (e.g., it's a cancelled timeout or animation frame callback)
|
||||
// then it will also be freed eventually.
|
||||
fn closure_once(cb: impl FnOnce() + 'static) -> JsValue {
|
||||
let mut wrapped_cb: Option<Box<dyn FnOnce()>> = Some(Box::new(cb));
|
||||
let closure = Closure::new(move || {
|
||||
if let Some(cb) = wrapped_cb.take() {
|
||||
cb()
|
||||
}
|
||||
});
|
||||
closure.into_js_value()
|
||||
}
|
||||
|
||||
/// Runs the given function between the next repaint using
|
||||
/// [`Window.requestAnimationFrame`](https://developer.mozilla.org/en-US/docs/Web/API/window/requestAnimationFrame),
|
||||
/// returning a cancelable handle.
|
||||
|
@ -128,7 +147,7 @@ pub fn request_animation_frame_with_handle(
|
|||
.map(AnimationFrameRequestHandle)
|
||||
}
|
||||
|
||||
raf(Closure::once_into_js(cb))
|
||||
raf(closure_once(cb))
|
||||
}
|
||||
|
||||
/// Handle that is generated by [request_idle_callback_with_handle] and can be
|
||||
|
@ -237,7 +256,7 @@ pub fn set_timeout_with_handle(
|
|||
.map(TimeoutHandle)
|
||||
}
|
||||
|
||||
st(Closure::once_into_js(cb), duration)
|
||||
st(closure_once(cb), duration)
|
||||
}
|
||||
|
||||
/// "Debounce" a callback function. This will cause it to wait for a period of `delay`
|
||||
|
|
Loading…
Reference in a new issue