mirror of
https://github.com/DioxusLabs/dioxus
synced 2024-11-27 06:30:20 +00:00
Merge pull request #1223 from DioxusLabs/jk/fix-use-future
Fix race condition in use_future by using use_state directly.
This commit is contained in:
commit
79909060cd
1 changed files with 21 additions and 94 deletions
|
@ -1,12 +1,8 @@
|
||||||
#![allow(missing_docs)]
|
#![allow(missing_docs)]
|
||||||
use dioxus_core::{ScopeState, TaskId};
|
use dioxus_core::{ScopeState, TaskId};
|
||||||
use std::{
|
use std::{any::Any, cell::Cell, future::Future, rc::Rc, sync::Arc};
|
||||||
any::Any,
|
|
||||||
cell::{Cell, RefCell},
|
use crate::{use_state, UseState};
|
||||||
future::{Future, IntoFuture},
|
|
||||||
rc::Rc,
|
|
||||||
sync::Arc,
|
|
||||||
};
|
|
||||||
|
|
||||||
/// A future that resolves to a value.
|
/// A future that resolves to a value.
|
||||||
///
|
///
|
||||||
|
@ -31,43 +27,30 @@ where
|
||||||
F: Future<Output = T> + 'static,
|
F: Future<Output = T> + 'static,
|
||||||
D: UseFutureDep,
|
D: UseFutureDep,
|
||||||
{
|
{
|
||||||
|
let val = use_state(cx, || None);
|
||||||
|
|
||||||
let state = cx.use_hook(move || UseFuture {
|
let state = cx.use_hook(move || UseFuture {
|
||||||
update: cx.schedule_update(),
|
update: cx.schedule_update(),
|
||||||
needs_regen: Cell::new(true),
|
needs_regen: Cell::new(true),
|
||||||
values: Default::default(),
|
state: val.clone(),
|
||||||
task: Cell::new(None),
|
task: Default::default(),
|
||||||
dependencies: Vec::new(),
|
dependencies: Vec::new(),
|
||||||
waker: Default::default(),
|
|
||||||
});
|
});
|
||||||
|
|
||||||
*state.waker.borrow_mut() = None;
|
|
||||||
|
|
||||||
if dependencies.clone().apply(&mut state.dependencies) || state.needs_regen.get() {
|
if dependencies.clone().apply(&mut state.dependencies) || state.needs_regen.get() {
|
||||||
// We don't need regen anymore
|
// kill the old one, if it exists
|
||||||
state.needs_regen.set(false);
|
if let Some(task) = state.task.take() {
|
||||||
|
cx.remove_future(task);
|
||||||
|
}
|
||||||
|
|
||||||
// Create the new future
|
// Create the new future
|
||||||
let fut = future(dependencies.out());
|
let fut = future(dependencies.out());
|
||||||
|
let val = val.clone();
|
||||||
// Clone in our cells
|
let task = state.task.clone();
|
||||||
let values = state.values.clone();
|
|
||||||
let schedule_update = state.update.clone();
|
|
||||||
let waker = state.waker.clone();
|
|
||||||
|
|
||||||
// Cancel the current future
|
|
||||||
if let Some(current) = state.task.take() {
|
|
||||||
cx.remove_future(current);
|
|
||||||
}
|
|
||||||
|
|
||||||
state.task.set(Some(cx.push_future(async move {
|
state.task.set(Some(cx.push_future(async move {
|
||||||
let res = fut.await;
|
val.set(Some(fut.await));
|
||||||
values.borrow_mut().push(Box::leak(Box::new(res)));
|
task.take();
|
||||||
|
|
||||||
// if there's a waker, we dont re-render the component. Instead we just progress that future
|
|
||||||
match waker.borrow().as_ref() {
|
|
||||||
Some(waker) => waker.wake_by_ref(),
|
|
||||||
None => schedule_update(),
|
|
||||||
}
|
|
||||||
})));
|
})));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -80,21 +63,12 @@ pub enum FutureState<'a, T> {
|
||||||
Regenerating(&'a T), // the old value
|
Regenerating(&'a T), // the old value
|
||||||
}
|
}
|
||||||
|
|
||||||
pub struct UseFuture<T> {
|
pub struct UseFuture<T: 'static> {
|
||||||
update: Arc<dyn Fn()>,
|
update: Arc<dyn Fn()>,
|
||||||
needs_regen: Cell<bool>,
|
needs_regen: Cell<bool>,
|
||||||
task: Cell<Option<TaskId>>,
|
task: Rc<Cell<Option<TaskId>>>,
|
||||||
dependencies: Vec<Box<dyn Any>>,
|
dependencies: Vec<Box<dyn Any>>,
|
||||||
waker: Rc<RefCell<Option<std::task::Waker>>>,
|
state: UseState<Option<T>>,
|
||||||
values: Rc<RefCell<Vec<*mut T>>>,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<T> Drop for UseFuture<T> {
|
|
||||||
fn drop(&mut self) {
|
|
||||||
for value in self.values.take().into_iter() {
|
|
||||||
drop(unsafe { Box::from_raw(value) })
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub enum UseFutureState<'a, T> {
|
pub enum UseFutureState<'a, T> {
|
||||||
|
@ -120,30 +94,16 @@ impl<T> UseFuture<T> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// clears the value in the future slot without starting the future over
|
|
||||||
pub fn clear(&self) -> Option<T> {
|
|
||||||
todo!()
|
|
||||||
// (self.update)();
|
|
||||||
// self.slot.replace(None)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Manually set the value in the future slot without starting the future over
|
// Manually set the value in the future slot without starting the future over
|
||||||
pub fn set(&self, _new_value: T) {
|
pub fn set(&self, new_value: T) {
|
||||||
// self.slot.set(Some(new_value));
|
self.state.set(Some(new_value));
|
||||||
// self.needs_regen.set(true);
|
|
||||||
// (self.update)();
|
|
||||||
todo!()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Return any value, even old values if the future has not yet resolved.
|
/// Return any value, even old values if the future has not yet resolved.
|
||||||
///
|
///
|
||||||
/// If the future has never completed, the returned value will be `None`.
|
/// If the future has never completed, the returned value will be `None`.
|
||||||
pub fn value(&self) -> Option<&T> {
|
pub fn value(&self) -> Option<&T> {
|
||||||
self.values
|
self.state.current_val.as_ref().as_ref()
|
||||||
.borrow_mut()
|
|
||||||
.last()
|
|
||||||
.cloned()
|
|
||||||
.map(|x| unsafe { &*x })
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Get the ID of the future in Dioxus' internal scheduler
|
/// Get the ID of the future in Dioxus' internal scheduler
|
||||||
|
@ -169,35 +129,6 @@ impl<T> UseFuture<T> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'a, T> IntoFuture for &'a UseFuture<T> {
|
|
||||||
type Output = &'a T;
|
|
||||||
type IntoFuture = UseFutureAwait<'a, T>;
|
|
||||||
fn into_future(self) -> Self::IntoFuture {
|
|
||||||
UseFutureAwait { hook: self }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub struct UseFutureAwait<'a, T> {
|
|
||||||
hook: &'a UseFuture<T>,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<'a, T> Future for UseFutureAwait<'a, T> {
|
|
||||||
type Output = &'a T;
|
|
||||||
|
|
||||||
fn poll(
|
|
||||||
self: std::pin::Pin<&mut Self>,
|
|
||||||
cx: &mut std::task::Context<'_>,
|
|
||||||
) -> std::task::Poll<Self::Output> {
|
|
||||||
match self.hook.values.borrow_mut().last().cloned() {
|
|
||||||
Some(value) => std::task::Poll::Ready(unsafe { &*value }),
|
|
||||||
None => {
|
|
||||||
self.hook.waker.replace(Some(cx.waker().clone()));
|
|
||||||
std::task::Poll::Pending
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub trait UseFutureDep: Sized + Clone {
|
pub trait UseFutureDep: Sized + Clone {
|
||||||
type Out;
|
type Out;
|
||||||
fn out(&self) -> Self::Out;
|
fn out(&self) -> Self::Out;
|
||||||
|
@ -343,10 +274,6 @@ mod tests {
|
||||||
let blah = "asd";
|
let blah = "asd";
|
||||||
});
|
});
|
||||||
|
|
||||||
let g2 = a.await;
|
|
||||||
|
|
||||||
let g = fut.await;
|
|
||||||
|
|
||||||
todo!()
|
todo!()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue