-
-
Notifications
You must be signed in to change notification settings - Fork 1.4k
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
Consider handling async service effects through futures #364
Comments
Hi @deniskolodin! I'd like to work on this one, but I'm a bit unfamiliar with the code. Thanks. |
I believe this would be difficult to implement without rewriting Yew to use |
Hi! Since yew now supports wasm-bindgen, it's possible to use async await alongside yew. Here's a little snippet that I've created to let rust handle the async task via futures. use wasm_timer::Delay;
use std::sync::Arc;
use futures::lock::Mutex;
use wasm_bindgen_futures::futures_0_3::{future_to_promise, spawn_local, JsFuture};
//
// typical yew setup, I created a minimal model that act as an counter.
//
#[wasm_bindgen]
pub fn run_yew_fut() -> Result<(), JsValue> {
spawn_local(async {
let scope = make_scope();
let guarded_scope = Arc::new(Mutex::new(scope));
let fut1 = call_scope_later(guarded_scope.clone());
let fut2 = log_later();
let chained = futures::future::join(fut1, fut2);
let resolved = chained.await;
});
Ok(())
}
fn make_scope() -> yew::html::Scope<Model> {
yew::initialize();
let app: App<Model> = App::new();
app.mount_to_body()
}
async fn log_later() {
console_log::init_with_level(Level::Debug).unwrap();
let mut counts: usize = 0;
loop {
Delay::new(Duration::from_secs(1)).await.unwrap();
counts += 1;
info!("logged: {} times", counts);
}
}
async fn call_scope_later(scope: Arc<Mutex<yew::html::Scope<Model>>>) {
let mut inner = scope.lock().await;
let mut counter = 0;
while counter < 10 {
Delay::new(Duration::from_secs(1)).await.unwrap();
inner.send_message(Msg::DoIt);
counter += 1;
}
} Although I'm not sure who is handling the UI loop since I didn't execute yew::run_loop(), but here's what I've got. You can see that the two async funtion are not blocking each other, either prevents the user from interacting with the component. Noted that wasm didn't have mature threading support right now, so I have to use local executor. Maybe after we have threading in wasm we can make this into a threaded UI library. |
It seems that wasm-bindgen >= 0.2.48 is required to support async-await. |
Thanks for calling this out @extraymond, later versions of wasm-bindgen were breaking stdweb so I fixed the version for now. Created an issue here: #586 |
Sure! Looking forward to future releases. It's getting better and better every releases. |
rustasync team had released a http request library surf, which can be used under wasm and async. Seems like the easiest way to integrate fetch as future. |
The only drawback to |
Thx for the update. I believe futures can be aborted by wrapping it in a Abortable. use futures::future::{AbortHandle, Abortable};
fn create_abort_handle() -> AbortHandle {
let (abort_handle, abort_registration) = AbortHandle::new_pair();
let req_futures = create_request(some_url);
let wrapped_req = Abortable::new(req_futures, abort_registration);
spawn_local(wrapped_req);
abort_handle
}
fn main() {
let abort_handle = create_abort_handle();
abort_handle.abort();
} I tried AbortHandle to cancel tasks, and it worked right away. Even wrapping promise to a JsFuture works. However I'm not sure if this will stop the browser from requesting, or just ignoring the incoming response into the wasm program. I think the latter case is ideal enough though. |
Having async operations available would also help in dealing with heavily asynchronous APIs accessed through web-sys. In the tests I'm currently doing that appears to work well, and I'm using code like async fn discover() -> Result<web_sys::BluetoothRemoteGattServer, String> {
[ lots of web-sys calls through JsFuture ]
// This still has to get better ergonomics, but one thing at a time…
let connection = gatt.connect();
let connection = wasm_bindgen_futures::JsFuture::from(connection).await
.map_err(|e| format!("Connect failed {:?}", e))?;
let connection = web_sys::BluetoothRemoteGattServer::from(connection);
Ok(connection)
}
async fn wrap<F: std::future::Future>(f: F, done_cb: yew::Callback<F::Output>) {
done_cb.emit(f.await);
}
impl Component for BleBridge {
[...]
fn update(&mut self, msg: Self::Message) -> ShouldRender {
match msg {
Msg::GattConnectClicked => {
wasm_bindgen_futures::spawn_local(wrap(discover(),
self.link.callback(|e| Msg::GattConnectDone(e))));
}
Msg::GattConnectDone(d) => {
match d {
Ok(d) => { info!("Got {:?}", d); }
Err(d) => { info!("Error: {:?}", d); }
}
}
}
false
}
} For some applications, it may suffice to document that this works (provided it's correct w/rt when emit may be called). This shouldn't stop any deeper integration of async patterns into yew, but may save current users the despair of thinking that there's no futures / async support around because none of it is mentioned in the documentation. |
I really like this solution (it feels satisfying as I read the short code excerpt) 🙃. |
@hamza1311 , if this has not been solved, you can hand it off to me. |
If someone is reading this page like I did, the example above does not fully work anymore, although the concepts are still accurate. But more support has been added in Yew and we have an example: Futures |
FWIW, I just opened #3609 that should make things in this domain better with upstream yew, if it lands :) |
Description
I'm submitting a feature request.
As it currently stands, asynchronous tasks are done through functions exposed by services, taking user-supplied callbacks which handle effects upon completion.
This approach is not very idiomatic. Besides, callback-structured code tends to be brittle and compose poorly.
The underlying Web APIs make use of promises. With the new wasm-bindgen-futures, it might be plausible to switch to using futures instead, without much implementation hassle. This would integrate well with the rest of the Rust async ecosystem.
The text was updated successfully, but these errors were encountered: