Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Make the ScopedFuture abort on cleanup of the owner #2378

Open
wants to merge 1 commit into
base: main
Choose a base branch
from

Conversation

TethysSvensson
Copy link

Currently the four spawn functions that use the ScopedFuture has some non-intuitive behavior, which this PR attempts to fix.

Persistence across re-evaluation of the owning effect

When you spawn a future with the current owner, the most intuitive behavior would be for it to live as long as e.g. signals created in the same closure.

Consider for instance the following code:

create_effect(move |_| {
    let value = some_signal.get();
    spawn_local_with_current_owner(async move {
        let log_stream = register_new_log_stream().await;
        loop {
            log_stream.send(format!("Value is {value:?}")).await;
            gloo::timers::future::TimeoutFuture::new(100).await
        }
    })
    .unwrap();
});

I would expect this code it to spawn a new future every time some_signal changes, which it does. However I would also expect it to stop the previous future which it does not. The result is that multiple futures will run concurrently.

Predictable clean-up

Currently long-running futures are not cleaned up at a predictable time.

Consider for instance the following code:

#[component]
fn LiveInfoPage() -> impl IntoView {
    let (state, set_state) = create_signal(State::default());

    spawn_local_with_current_owner(async move {
        let resource = initialize_expensive_resource().await;
        let connection = open_websocket().await;
        while let Some(msg) = connection.next_update().await {
            let state = resource.process(msg);
            set_state.set(state);
        }
    })
    .unwrap();

    "..."
}

Ideally the resources initialized by the future would be cleaned up as soon as the user navigates away from the LiveInfoPage, however that is not how ScopedFuture currently works.

Instead the resources only cleaned up once the next_update() future makes progress at which point the ScopedFuture will finally finish.

Solution

Make sure the future is woken up and aborted whenever the cleanup effects run on the owner.

Alternatives

Document the current behavior, and potentially add new functions with the behavior implemented in this PR.

@gbj gbj added this to the 0.7 milestone Mar 1, 2024
@gbj
Copy link
Collaborator

gbj commented Mar 1, 2024

Given the breaking changes and additional overhead, I'm going to tag this one as 0.7 to make sure it behaves as expected in the future. Alternatively, feel free to duplicate + rename these functions, leaving the original ones as they are.

@TethysSvensson
Copy link
Author

I understand that you might want to keep the current behavior until v0.7. In case somebody is depending on the current behavior is a breaking change.

I personally didn't think of it as a breaking change though, as I cannot think of a single use-case where the current behavior is preferable. Hence I thought of it as a bugfix rather change a breaking change.

Would you prefer to add the new behavior now under different names, or keep this PR open until v0.7? It won't matter much to us, as we don't mind having a wrapper for ScopedFuture until v0.7

If you prefer different names, do you have a suggestion for what to name them?

@gbj
Copy link
Collaborator

gbj commented Mar 1, 2024

To be clear I mean that it's a breaking change primarily because it changes the signature of public functions, and secondarily because of the behavior change. Something like _abortable for the function names maybe?

@TethysSvensson
Copy link
Author

Do you want me to write also update the docs for the non abortable versions?

@TethysSvensson
Copy link
Author

Also, what is the timeline on releasing 0.7? If it's happening within the next few months, perhaps it would be best to just merge this PR once you get close to a release.

@gbj
Copy link
Collaborator

gbj commented Mar 8, 2024

Sure, I think updating the docs for the non-abortable version would be great.

And re: a timeline for 0.7, I don't have a very firm answer but "within the next few months" seems like it's in the right zone. I'd say: if the abortable version is something you need right now, I'm happy to take the PR now, and can then decide whether to make this the default behavior in the next version.

@TethysSvensson
Copy link
Author

It is something I need right now, but it is also something I don't mind keeping around as a utility function in my own crate for a few months until v0.7 gets released.

I would prefer if v0.7 contained my versions, as I think the current implementation is a footgun. Alternatively I would also think it would be fine, if the current implement was available under a different name that, to make it less likely for somebody to get the wrong impression about their behavior.

@gbj
Copy link
Collaborator

gbj commented Apr 29, 2024

Just leaving an update:

0.7 is a significant enough rewrite that the functions this PR modifies in leptos_reactive are not used (that entire crate may no longer be in existence when actually released)

The idea of the PR (aborting Futures on cleanup) is probably worth including as an opt in, and possibly even by default.

It's worth saying that this can introduce its own confusions and will need to be documented clearly as to what it does/does not do: for example, if this is used with a fetch request aborting the Future will not abort the request, unless a browser AbortSignal is used.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

None yet

2 participants