-
-
Notifications
You must be signed in to change notification settings - Fork 1.2k
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
native/subscription/unfold: terminate stream when user returns None
#1553
native/subscription/unfold: terminate stream when user returns None
#1553
Conversation
couldn't you just put a simple if statement that when your subscription is finished you no longer return it? if self.continue_websocket {
echo::connect().map(Message::Echo)
} else {
iced::subscription::Subscription::none()
} |
Thanks for the suggestion @Night-Hunter-NF. I faced the issue addressed in this PR while already doing this. |
f843652
to
043b691
Compare
043b691
to
3ab18d2
Compare
3ab18d2
to
0246fc1
Compare
This commit shows how the subscription `Future` is called repeatedly after the `Finished` state is reached, even after returning a `None` message. This is the reason for the `pending().await` workaround in the original code. See next commit for a fix which allows returning `None` to stop the `Stream`, similarly to how `futures::stream::unfold` behaves.
`futures::stream::unfold` builds a `Stream` from the provided seed of type `T` and a `Future` returned by the `f` function. The resulting `Stream` produces values as long the `Future` returns `Some` tuple `(Item, T)`. If the `Future` returns `None`, `unfold` is supposed to stop producing items. In `iced`, the `subscription::unfold` function is based on the `stream::unfold`. However the `Output` of the `Future` is defined as `(Option<Message>, T)`. The `Future` can return a `None` `Message` and the `Stream` will keep running. The above behaviour is exemplified in `examples/download_progress`. When the download is finished (either because it is complete or if an error occurred), the `Future` is still being called. Adding a `println` statement shows that the `Future` is called repeatedly. The original code added a `pending().await` as a workaround. This means that the executor `Tasks` is kept in queues even if it will no longer make progress. One way to solve this would be to keep the existing signature for `subscription::unfold` and to `map` the `Future` output in such a way that it return `None` if the provided `Option<Message>` is `None`. However, this would be inconsistent with the `unfold` function from the `futures` crate and it wouldn't be suitable since the `state` is no longer usable after the `Stream` completes. This commit aligns the `subscription::unfold` function with its `futures` counterpart. In the `download_progress` example, after a download is finished, the `Task` in no longer kept in queues. This changes a bit the implementation for the `websocket` example. Previous implementation returned from the `Future` with a `None` message and an unchanged state when the websocket or input are awaken but the result doesn't need to be propagated. With this commit, in such cases the implementation loops on the wbesocket & input async select awaiting for the next message to be propagated.
The `on_press` handler was not defined for the 'Start again' button, so the button was inactive after a download completion.
0246fc1
to
6ecb44e
Compare
While the changes at first glance seem reasonable and consistent, the main issue here is that a Therefore, exposing an API that seems to indicate subscriptions can "end" by themselves seems misleading. It is true that we are already exposing |
I'd like to know more about these issues. Is there something you need to do that the current |
I believe the main issue here is that I have made that change in #1786 and introduced a new helper |
Context
While experimenting with
iced
for a WIP application, I ran into issues with thesubscription::unfold
behaviour. This MR proposes a change to the API which would solve my issues and which I believe would be preferable foriced
in general.Note: I read the contributing guidelines. Since this seemed like a small improvement to me, I didn't feel the need to introduce myself on the Discord server. In order to reflect on this I had to experiment to the extent of this MR anyway. Please tell me if I was wrong!
Analysis
futures::stream::unfold
builds aStream
from the provided seed of typeT
and aFuture
returned by thef
function. The resultingStream
produces values as long theFuture
returnsSome
tuple(Item, T)
. If theFuture
returnsNone
,unfold
is supposed to stop producing items.In
iced
, thesubscription::unfold
function is based on thestream::unfold
. However theOutput
of theFuture
is defined as(Option<Message>, T)
. TheFuture
can return aNone
Message
and theStream
will keep running.The above behaviour is exemplified in
examples/download_progress
. When the download is finished (either because it is complete or if an error occurred), theFuture
is still being called. Adding aprintln
statement shows that theFuture
is called repeatedly. The original code added apending().await
as a workaround. This means that the executorTasks
is kept in queues even if it will no longer make progress.The first commit removes the
pending().await
workaround indownload_progress
and addsstdout
&stderr
traces so as to assess the impact of the second commit.Proposal
One way to solve this would be to keep the existing signature for
subscription::unfold
and tomap
theFuture
output in such a way that it returnNone
if the providedOption<Message>
isNone
. However, this would be inconsistent with theunfold
function from thefutures
crate and it wouldn't be suitable since thestate
is no longer usable after theStream
completes.
This MR aligns the
subscription::unfold
function with itsfutures
counterpart. In thedownload_progress
example, after a download is finished, theTask
in no longer kept in queues.This changes a bit the implementation for the
websocket
example. Previous implementation returned from theFuture
with aNone
message and an unchanged state when the websocket or input were awaken but the result didn't need to be propagated. With this MR, in such cases the implementation loops on the wbesocket & input async select awaiting for the next message to be propagated.The last commit is a small unrelated improvement to the
download_progress
example.