Rename modules to use snake_case (#1498)
This commit is contained in:
1 parent
31780b3ede
commit
a2dd926f7f
13 files changed
+18
-18
No files matched your search
@@ -0,0 +1,163 @@
|
||||
use dioxus_core::{ScopeState, TaskId};
|
||||
pub use futures_channel::mpsc::{UnboundedReceiver, UnboundedSender};
|
||||
use std::future::Future;
|
||||
|
||||
/// Maintain a handle over a future that can be paused, resumed, and canceled.
|
||||
///
|
||||
/// This is an upgraded form of [`use_future`] with an integrated channel system.
|
||||
/// Specifically, the coroutine generated here comes with an [`UnboundedChannel`]
|
||||
/// built into it - saving you the hassle of building your own.
|
||||
///
|
||||
/// Addititionally, coroutines are automatically injected as shared contexts, so
|
||||
/// downstream components can tap into a coroutine's channel and send messages
|
||||
/// into a singular async event loop.
|
||||
///
|
||||
/// This makes it effective for apps that need to interact with an event loop or
|
||||
/// some asynchronous code without thinking too hard about state.
|
||||
///
|
||||
/// ## Global State
|
||||
///
|
||||
/// Typically, writing apps that handle concurrency properly can be difficult,
|
||||
/// so the intention of this hook is to make it easy to join and poll async tasks
|
||||
/// concurrently in a centralized place. You'll find that you can have much better
|
||||
/// control over your app's state if you centralize your async actions, even under
|
||||
/// the same concurrent context. This makes it easier to prevent undeseriable
|
||||
/// states in your UI while various async tasks are already running.
|
||||
///
|
||||
/// This hook is especially powerful when combined with Fermi. We can store important
|
||||
/// global data in a coroutine, and then access display-level values from the rest
|
||||
/// of our app through atoms.
|
||||
///
|
||||
/// ## UseCallback instead
|
||||
///
|
||||
/// However, you must plan out your own concurrency and synchronization. If you
|
||||
/// don't care about actions in your app being synchronized, you can use [`use_callback`]
|
||||
/// hook to spawn multiple tasks and run them concurrently.
|
||||
///
|
||||
/// ### Notice
|
||||
/// In order to use ``rx.next().await``, you will need to extend the ``Stream`` trait (used by ``UnboundedReceiver``)
|
||||
/// by adding the ``futures-util`` crate as a dependency and adding ``StreamExt`` into scope via ``use futures_util::stream::StreamExt;``
|
||||
///
|
||||
/// ## Example
|
||||
///
|
||||
/// ```rust, ignore
|
||||
/// enum Action {
|
||||
/// Start,
|
||||
/// Stop,
|
||||
/// }
|
||||
///
|
||||
/// let chat_client = use_coroutine(cx, |mut rx: UnboundedReceiver<Action>| async move {
|
||||
/// while let Some(action) = rx.next().await {
|
||||
/// match action {
|
||||
/// Action::Start => {}
|
||||
/// Action::Stop => {},
|
||||
/// }
|
||||
/// }
|
||||
/// });
|
||||
///
|
||||
///
|
||||
/// cx.render(rsx!{
|
||||
/// button {
|
||||
/// onclick: move |_| chat_client.send(Action::Start),
|
||||
/// "Start Chat Service"
|
||||
/// }
|
||||
/// })
|
||||
/// ```
|
||||
pub fn use_coroutine<M, G, F>(cx: &ScopeState, init: G) -> &Coroutine<M>
|
||||
where
|
||||
M: 'static,
|
||||
G: FnOnce(UnboundedReceiver<M>) -> F,
|
||||
F: Future<Output = ()> + 'static,
|
||||
{
|
||||
cx.use_hook(|| {
|
||||
let (tx, rx) = futures_channel::mpsc::unbounded();
|
||||
let task = cx.push_future(init(rx));
|
||||
cx.provide_context(Coroutine { tx, task })
|
||||
})
|
||||
}
|
||||
|
||||
/// Get a handle to a coroutine higher in the tree
|
||||
///
|
||||
/// See the docs for [`use_coroutine`] for more details.
|
||||
pub fn use_coroutine_handle<M: 'static>(cx: &ScopeState) -> Option<&Coroutine<M>> {
|
||||
cx.use_hook(|| cx.consume_context::<Coroutine<M>>())
|
||||
.as_ref()
|
||||
}
|
||||
|
||||
pub struct Coroutine<T> {
|
||||
tx: UnboundedSender<T>,
|
||||
task: TaskId,
|
||||
}
|
||||
|
||||
// for use in futures
|
||||
impl<T> Clone for Coroutine<T> {
|
||||
fn clone(&self) -> Self {
|
||||
Self {
|
||||
tx: self.tx.clone(),
|
||||
task: self.task,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> Coroutine<T> {
|
||||
/// Get the ID of this coroutine
|
||||
#[must_use]
|
||||
pub fn task_id(&self) -> TaskId {
|
||||
self.task
|
||||
}
|
||||
|
||||
/// Send a message to the coroutine
|
||||
pub fn send(&self, msg: T) {
|
||||
let _ = self.tx.unbounded_send(msg);
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> PartialEq for Coroutine<T> {
|
||||
fn eq(&self, other: &Self) -> bool {
|
||||
self.task == other.task
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
#![allow(unused)]
|
||||
|
||||
use super::*;
|
||||
use dioxus_core::prelude::*;
|
||||
use futures_channel::mpsc::unbounded;
|
||||
use futures_util::StreamExt;
|
||||
|
||||
fn app(cx: Scope, name: String) -> Element {
|
||||
let task = use_coroutine(cx, |mut rx: UnboundedReceiver<i32>| async move {
|
||||
while let Some(msg) = rx.next().await {
|
||||
println!("got message: {msg}");
|
||||
}
|
||||
});
|
||||
|
||||
let task2 = use_coroutine(cx, view_task);
|
||||
|
||||
let task3 = use_coroutine(cx, |rx| complex_task(rx, 10));
|
||||
|
||||
todo!()
|
||||
}
|
||||
|
||||
async fn view_task(mut rx: UnboundedReceiver<i32>) {
|
||||
while let Some(msg) = rx.next().await {
|
||||
println!("got message: {msg}");
|
||||
}
|
||||
}
|
||||
|
||||
enum Actions {
|
||||
CloseAll,
|
||||
OpenAll,
|
||||
}
|
||||
|
||||
async fn complex_task(mut rx: UnboundedReceiver<Actions>, name: i32) {
|
||||
while let Some(msg) = rx.next().await {
|
||||
match msg {
|
||||
Actions::CloseAll => todo!(),
|
||||
Actions::OpenAll => todo!(),
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in new issue
Block a user