Skip to content

Commit

Permalink
Add an example for reactor agent.
Browse files Browse the repository at this point in the history
  • Loading branch information
futursolo committed Aug 20, 2023
1 parent dd627aa commit 1d3f081
Show file tree
Hide file tree
Showing 11 changed files with 182 additions and 9 deletions.
17 changes: 17 additions & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

3 changes: 2 additions & 1 deletion examples/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -62,7 +62,8 @@ As an example, check out the TodoMVC example here: <https://examples.yew.rs/todo
| [timer_functional](timer_functional) | [F] | Demonstrates the use of the interval and timeout services using function components |
| [todomvc](todomvc) | [S] | Implementation of [TodoMVC](http://todomvc.com/). |
| [two_apps](two_apps) | [S] | Runs two separate Yew apps which can communicate with each other. |
| [web_worker_fib](web_worker_fib) | [S] | Calculate Fibonacci numbers in a web worker thread using [`gloo-worker`](https://docs.rs/gloo-worker/latest/gloo_worker/). |
| [web_worker_fib](web_worker_fib) | [S] | Calculate Fibonacci numbers in a web worker thread using [`yew-agent`](https://docs.rs/gloo-worker/latest/yew_agent/). |
| [web_worker_prime](web_worker_prime) | [F] | Calculate Prime numbers in a web worker thread using [`yew-agent`](https://docs.rs/gloo-worker/latest/yew_agent/). |
| [webgl](webgl) | [S] | Controls a [WebGL canvas](https://developer.mozilla.org/en-US/docs/Web/API/WebGL_API/Tutorial/Getting_started_with_WebGL) from Yew. |

[CT]: ## "Component Type"
Expand Down
11 changes: 11 additions & 0 deletions examples/web_worker_prime/Cargo.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
[package]
name = "yew-worker-prime"
version = "0.1.0"
edition = "2021"

[dependencies]
yew-agent = { path = "../../packages/yew-agent" }
yew = { path = "../../packages/yew", features = ["csr"] }
futures = "0.3.25"
primes = "0.3.0"
serde = { version = "1.0.147", features = ["derive"] }
17 changes: 17 additions & 0 deletions examples/web_worker_prime/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
# Web Worker Prime

[![Demo](https://img.shields.io/website?label=demo&url=https%3A%2F%2Fexamples.yew.rs%2Fweb_worker_prime)](https://examples.yew.rs/web_worker_prime)

Calculate primes until stop button is pressed, without blocking the main thread.

## Concepts

The example illustrates how to use reactor agents to offload CPU bound tasks to a worker thread in a Yew application.

## Running

Run this application with the trunk development server:

```bash
trunk serve --open
```
15 changes: 15 additions & 0 deletions examples/web_worker_prime/index.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
<!doctype html>
<html lang="en">

<head>
<meta charset="utf-8">
<title>Yew • Web Worker Prime</title>

<link data-trunk rel="rust" href="Cargo.toml" data-bin="app" data-type="main" data-weak-refs />
<link data-trunk rel="rust" href="Cargo.toml" data-bin="worker" data-type="worker" data-weak-refs />
</head>

<body>
</body>

</html>
38 changes: 38 additions & 0 deletions examples/web_worker_prime/src/agent.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
use std::time::Duration;

use futures::sink::SinkExt;
use futures::{FutureExt, StreamExt};
use serde::{Deserialize, Serialize};
use yew::platform::time::sleep;
use yew_agent::prelude::*;

#[derive(Debug, PartialEq, Eq, Serialize, Deserialize)]
pub enum ControlSignal {
Start,
Stop,
}

#[reactor]
pub async fn Prime(mut scope: ReactorScope<ControlSignal, u64>) {
while let Some(m) = scope.next().await {
if m == ControlSignal::Start {
'inner: for i in 1.. {
// This is not the most efficient way to calculate prime,
// but this example is here to demonstrate how primes can be
// sent to the application in an ascending order.
if primes::is_prime(i) {
scope.send(i).await.unwrap();
}

futures::select! {
m = scope.next() => {
if m == Some(ControlSignal::Stop) {
break 'inner;
}
},
_ = sleep(Duration::from_millis(100)).fuse() => {},
}
}
}
}
}
3 changes: 3 additions & 0 deletions examples/web_worker_prime/src/bin/app.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
fn main() {
yew::Renderer::<yew_worker_prime::App>::new().render();
}
6 changes: 6 additions & 0 deletions examples/web_worker_prime/src/bin/worker.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
use yew_agent::Registrable;
use yew_worker_prime::agent::Prime;

fn main() {
Prime::registrar().register();
}
64 changes: 64 additions & 0 deletions examples/web_worker_prime/src/lib.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
pub mod agent;
use agent::{ControlSignal, Prime};
use yew::prelude::*;
use yew_agent::reactor::{use_reactor_subscription, ReactorProvider};

#[function_component]
fn Main() -> Html {
let prime_sub = use_reactor_subscription::<Prime>();
let started = use_state_eq(|| false);
let skip_len = use_state_eq(|| 0);

let result_s = prime_sub
.iter()
// Skip results in previous runs.
.skip(*skip_len)
.fold("".to_string(), |mut output, item| {
if !output.is_empty() {
output.push_str(", ");
}

output.push_str(&item.to_string());

output
});

let start_prime_calc = use_callback(
(prime_sub.clone(), started.setter(), skip_len.setter()),
|_input, (prime_sub, started_setter, skip_len)| {
skip_len.set(prime_sub.len());
prime_sub.send(ControlSignal::Start);
started_setter.set(true);
},
);

let stop_prime_calc = use_callback(
(prime_sub, started.setter()),
|_input, (prime_sub, started_setter)| {
prime_sub.send(ControlSignal::Stop);
started_setter.set(false);
},
);

html! {
<>
<h1>{"Find Prime"}</h1>
<p>{"This page demonstrates how to calculate prime in a web worker."}</p>
if *started {
<button onclick={stop_prime_calc}>{"Stop"}</button>
} else {
<button onclick={start_prime_calc}>{"Start"}</button>
}
<div id="result">{result_s}</div>
</>
}
}

#[function_component]
pub fn App() -> Html {
html! {
<ReactorProvider<Prime> path="/worker.js">
<Main />
</ReactorProvider<Prime>>
}
}
11 changes: 6 additions & 5 deletions packages/yew-agent/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -56,11 +56,12 @@
//! Similar to bridges, a subscription produces a handle to send inputs to agents. However, instead
//! of notifying the receiver with a callback, it collect all outputs into a slice.
//!
//! #### Task
//! #### Oneshot
//!
//! See: [`use_task`](task::use_task)
//! See: [`use_oneshot_bridge`](oneshot::use_oneshot_bridge)
//!
//! Unlike other agents, tasks provides a `use_task` hook to execute tasks on demand.
//! Unlike other agents, oneshot bridges provide a `use_oneshot_bridge` hook to execute oneshot
//! agents on demand.

#![deny(
clippy::all,
Expand Down Expand Up @@ -99,13 +100,13 @@ pub mod prelude {
pub use crate::oneshot::{oneshot, use_oneshot_bridge, UseOneshotBridgeHandle};
pub use crate::reach::Reach;
pub use crate::reactor::{
reactor, use_reactor_bridge, use_reactor_subscription, ReactorEvent,
reactor, use_reactor_bridge, use_reactor_subscription, ReactorEvent, ReactorScope,
UseReactorBridgeHandle, UseReactorSubscriptionHandle,
};
pub use crate::scope_ext::{AgentScopeExt, ReactorBridgeHandle, WorkerBridgeHandle};
pub use crate::worker::{
use_worker_bridge, use_worker_subscription, UseWorkerBridgeHandle,
UseWorkerSubscriptionHandle,
UseWorkerSubscriptionHandle, WorkerScope,
};
pub use crate::{Registrable, Spawnable};
}
6 changes: 3 additions & 3 deletions packages/yew-agent/src/scope_ext.rs
Original file line number Diff line number Diff line change
Expand Up @@ -84,8 +84,8 @@ pub trait AgentScopeExt {
R: Reactor + 'static,
<R::Scope as ReactorScoped>::Output: 'static;

/// Runs a task in a Task Agent.
fn run_task<T>(&self, input: T::Input, callback: Callback<T::Output>)
/// Runs an oneshot in an Oneshot Agent.
fn run_oneshot<T>(&self, input: T::Input, callback: Callback<T::Output>)
where
T: Oneshot + 'static;
}
Expand Down Expand Up @@ -132,7 +132,7 @@ where
ReactorBridgeHandle { tx }
}

fn run_task<T>(&self, input: T::Input, callback: Callback<T::Output>)
fn run_oneshot<T>(&self, input: T::Input, callback: Callback<T::Output>)
where
T: Oneshot + 'static,
{
Expand Down

0 comments on commit 1d3f081

Please sign in to comment.