This is unreleased documentation for Yew Next version.
For up-to-date documentation, see the latest version on docs.rs.

yew_agent/worker/
scope.rs

1use std::cell::RefCell;
2use std::fmt;
3use std::future::Future;
4use std::rc::Rc;
5
6use serde::de::Deserialize;
7use serde::ser::Serialize;
8use wasm_bindgen_futures::spawn_local;
9
10use super::handler_id::HandlerId;
11use super::lifecycle::{WorkerLifecycleEvent, WorkerRunnable, WorkerState};
12use super::messages::FromWorker;
13use super::native_worker::{DedicatedWorker, NativeWorkerExt, WorkerSelf};
14use super::traits::Worker;
15use super::Shared;
16use crate::codec::Codec;
17
18/// A handle that closes the worker when it is dropped.
19pub struct WorkerDestroyHandle<W>
20where
21    W: Worker + 'static,
22{
23    scope: WorkerScope<W>,
24}
25
26impl<W: Worker> fmt::Debug for WorkerDestroyHandle<W> {
27    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
28        f.write_str("WorkerDestroyHandle<_>")
29    }
30}
31
32impl<W> WorkerDestroyHandle<W>
33where
34    W: Worker,
35{
36    pub(crate) fn new(scope: WorkerScope<W>) -> Self {
37        Self { scope }
38    }
39}
40
41impl<W> Drop for WorkerDestroyHandle<W>
42where
43    W: Worker,
44{
45    fn drop(&mut self) {
46        self.scope.send(WorkerLifecycleEvent::Destroy);
47    }
48}
49
50/// This struct holds a reference to a component and to a global scheduler.
51pub struct WorkerScope<W: Worker> {
52    state: Shared<WorkerState<W>>,
53    post_msg: Rc<dyn Fn(FromWorker<W>)>,
54}
55
56impl<W: Worker> fmt::Debug for WorkerScope<W> {
57    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
58        f.write_str("WorkerScope<_>")
59    }
60}
61
62impl<W: Worker> Clone for WorkerScope<W> {
63    fn clone(&self) -> Self {
64        WorkerScope {
65            state: self.state.clone(),
66            post_msg: self.post_msg.clone(),
67        }
68    }
69}
70
71impl<W> WorkerScope<W>
72where
73    W: Worker + 'static,
74{
75    /// Create worker scope
76    pub(crate) fn new<CODEC>() -> Self
77    where
78        CODEC: Codec,
79        W::Output: Serialize + for<'de> Deserialize<'de>,
80    {
81        let post_msg = move |msg: FromWorker<W>| {
82            DedicatedWorker::worker_self().post_packed_message::<_, CODEC>(msg)
83        };
84
85        let state = Rc::new(RefCell::new(WorkerState::new()));
86        WorkerScope {
87            post_msg: Rc::new(post_msg),
88            state,
89        }
90    }
91
92    /// Schedule message for sending to worker
93    pub(crate) fn send(&self, event: WorkerLifecycleEvent<W>) {
94        let state = self.state.clone();
95
96        // We can implement a custom scheduler,
97        // but it's easier to borrow the one from wasm-bindgen-futures.
98        spawn_local(async move {
99            WorkerRunnable { state, event }.run();
100        });
101    }
102
103    /// Send response to a worker bridge.
104    pub fn respond(&self, id: HandlerId, output: W::Output) {
105        let msg = FromWorker::<W>::ProcessOutput(id, output);
106        (self.post_msg)(msg);
107    }
108
109    /// Send a message to the worker
110    pub fn send_message<T>(&self, msg: T)
111    where
112        T: Into<W::Message>,
113    {
114        self.send(WorkerLifecycleEvent::Message(msg.into()));
115    }
116
117    /// Create a callback which will send a message to the worker when invoked.
118    pub fn callback<F, IN, M>(&self, function: F) -> Rc<dyn Fn(IN)>
119    where
120        M: Into<W::Message>,
121        F: Fn(IN) -> M + 'static,
122    {
123        let scope = self.clone();
124        let closure = move |input| {
125            let output = function(input).into();
126            scope.send(WorkerLifecycleEvent::Message(output));
127        };
128        Rc::new(closure)
129    }
130
131    /// This method creates a callback which returns a Future which
132    /// returns a message to be sent back to the worker
133    ///
134    /// # Panics
135    /// If the future panics, then the promise will not resolve, and
136    /// will leak.
137    pub fn callback_future<FN, FU, IN, M>(&self, function: FN) -> Rc<dyn Fn(IN)>
138    where
139        M: Into<W::Message>,
140        FU: Future<Output = M> + 'static,
141        FN: Fn(IN) -> FU + 'static,
142    {
143        let scope = self.clone();
144
145        let closure = move |input: IN| {
146            let future: FU = function(input);
147            scope.send_future(future);
148        };
149
150        Rc::new(closure)
151    }
152
153    /// This method processes a Future that returns a message and sends it back to the worker.
154    ///
155    /// # Panics
156    /// If the future panics, then the promise will not resolve, and will leak.
157    pub fn send_future<F, M>(&self, future: F)
158    where
159        M: Into<W::Message>,
160        F: Future<Output = M> + 'static,
161    {
162        let scope = self.clone();
163        let js_future = async move {
164            let message: W::Message = future.await.into();
165            let cb = scope.callback(|m: W::Message| m);
166            (*cb)(message);
167        };
168        wasm_bindgen_futures::spawn_local(js_future);
169    }
170}