-
Notifications
You must be signed in to change notification settings - Fork 155
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
allow perform_cmd to send multiple messages #556
Comments
Maybe an idea would be providing a function like this: orders.perform_cmd_with_sink(async move |sink: MessageSink<Msg>| {
let msg = task1().await;
sink.send(msg).await?;
sink.send(task2().await).await?;
Ok(())
}); Here you have to provide a closure taking a If that was possible you could send as many messages as you like very easily. But futures can be very tricky, so I don't know how easy this would be to implement. |
@mankinskin that's a interesting idea! are you thinking that For example, here's something that's hard to express right now (or at least, I can't figure out how to express it nicely): I have a server that saves notes, as a struct Model {
notes: HashMap<Uuid, String>,
current_note: Option<Uuid>,
} enum Msg {
FetchNotes, // uses perform_cmd to fetch notes from the server, and call NotesFetched when the fetch is successful
NotesFetched(HashMap<Uuid, String>), // set model.notes to the given notes, and re-render the notes list
OpenNote(Uuid), // Set model.current_note to given Uuid, and render the note text
NewNote, // send a request to the server to generate a new note, with a server-generated uuid and text
} Say I want to set orders.perform_cmd_with_sink(async move |sink: MessageSink<Msg>| {
let uuid = call_new_note_api().await;
sink.send(Msg::FetchNotes).await?; // after this runs, we know that NotesFetched has been enqueued
sink.send(Msg::OpenNote(uuid)).await?;
Ok(())
}); Which would be pretty neat :) But I'm also curious how other people would go about solving this, possibly there's an easy way of getting around this problem that I'm not thinking of. |
For your use case I think it would be best to just store the uuid in the model and use it to open a note when you receive And I think you can actually already send multiple messages with a let msg_sender = orders.msg_sender();
orders.perform_cmd(async move {
let uuid = get_uuid().await;
msg_sender(Some(Msg::FetchNotes));
msg_sender(Some(Msg::OpenNote(uuid)));
None
}); To control execution order and handle message dependencies and such, I think we still have to call the subsequent messages after receiving the awaited message. So basically in your update function, after you receive But you are right, it would be much better if we could send messages and await results as you described. That is basically what asynchronous actor frameworks do, but I haven't found one for webassembly. |
@WesleyAC I'm glad you like it!
I don't see any real reasons to not allow it, however there is a problem:
...
sink.send(Msg::FetchNotes).await?;
sink.send(Msg::OpenNote(uuid)).await?;
... I was thinking a bit about Future chaining, but it would be a footgun because WASM still officially supports only a single thread just like JS so it would be too easy to freeze UI. I think we should wait for full multi-threading support and then adapt the whole framework to it. |
Could you give a simple example that would result in UI freeze? Also note that allowing sending multiple messages and awaiting on processing them is equivalent to adding new messages. enum Msg { A, B, C }
fn update(msg: Msg, ...) {
match msg {
Msg::A => orders.perform_cmd_with_sink(async move |sink| {
X;
sink.send(Msg::B).await;
Y;
sink.send(Msg::C).await;
Z;
}
Msg::B => { P; }
Msg::C => { Q; }
}
} is equivalent to: enum Msg { A, A1, A2, B { after: Box<Msg> }, C { after: Box<Msg> }}
fn update(msg: Msg, ...) {
match msg {
Msg::A => orders.perform_cmd(async move {
X;
Msg::B { after: Box::new(Msg::A1) }
})
Msg::A1 => orders.perform_cmd(async move {
Y;
Msg::C { after: Box::new(Msg::A2) }
})
Msg::A2 => orders.perform_cmd(async move {
Z;
})
Msg::B { after } => {
P;
orders.send_msg(*after);
}
Msg::C { after } => {
Q;
orders.send_msg(*after);
}
}
} The most problematic thing is that... ...
sink.send(Msg::FetchNotes).await?;
// ...here we don't have access to model and orders (because futures must be 'static)...
sink.send(Msg::OpenNote(uuid)).await?;
... ...so:
The most helping and quite simple thing that Seed can do (from my perspective), is to allow sending message via |
It would be great if
perform_cmd
had the option of sending multiple messages, as well as just one message. Sometimes I want to useperform_cmd
to grab some data from the server, and then trigger multiple messages at once based on that data. Right now, I have to make a new message for each combination of messages, which is just sort of annoying and clunky.I don't know what the best way of doing this is (Maybe allow
perform_cmd
to return()
,Msg
, orVec<Msg>
?), but if someone wants to point me in a good direction, I'd be happy to take a stab at implementing it :)BTW, Seed's been really great so far, I love how easy it is to use, and how good the documentation is!
The text was updated successfully, but these errors were encountered: