Last active
December 7, 2022 06:53
-
-
Save tomaka/25e0f01c427a1bce0e0ddb10d278ead9 to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// Suppose you have a variable named `future` which implements the `Future` trait. | |
let future: impl Future = ...; | |
// This gist demonstrates how to run the future until completion using the `stdweb` crate. | |
// The various imports. | |
extern crate futures; | |
extern crate stdweb; | |
use futures::executor; | |
use futures::future::Future; | |
use futures::{Stream, Sink, Async}; | |
use std::fmt::Debug; | |
use std::sync::{Arc, Mutex}; | |
use stdweb::web::set_timeout; | |
// The actual code. | |
// You would typically put this code at the end of the `main()` function, after having created `future`. | |
// We start by creating what is called a "task" (a concept of the futures crate). This takes ownership of the future. | |
let future_task = executor::spawn(future); | |
// We then create an instance of the `Notifier` struct. | |
// The `me` field holds an abstract version of the `Notifier` itself (creating a cyclic redundancy, but who cares since | |
// this is the last thing we do), and `task` contains the `future_task` we created. | |
struct Notifier<T> { me: Mutex<Option<executor::NotifyHandle>>, task: Arc<Mutex<executor::Spawn<T>>> } | |
let notifier = Arc::new(Notifier { me: Mutex::new(None), task: Arc::new(Mutex::new(future_task)) }); | |
// As explained, we store the `notifier` into itself. | |
let notify_handle = executor::NotifyHandle::from(notifier.clone()); | |
*notifier.me.lock().unwrap() = Some(notify_handle.clone()); | |
// Now we call the `notify` method on the notifier ; the method is defined below. | |
notify_handle.notify(0); | |
// When you are within emscripten, the `main()` function can be considered as a function that initializes | |
// everything rather than runs everything. Calling `event_loop()` indicates that this initialization is | |
// over ; it stops the execution of the `main()` function and allows the browser/interpreter to start running | |
// callbacks (including the call to `set_timeout()` within the `notify` method called at the previous line). | |
// It is these callbacks that will drive the execution to completion. | |
stdweb::event_loop(); | |
unsafe impl<T> Send for Notifier<T> {} | |
unsafe impl<T> Sync for Notifier<T> {} | |
impl<T> executor::Notify for Notifier<T> | |
where T: Future, | |
T::Item: Debug, | |
T::Error: Debug, | |
{ | |
fn notify(&self, _: usize) { | |
// This method is first called at initialization, then later whenever something calls | |
// the `Task::notify()` method of the `futures` crate. | |
let task = self.task.clone(); | |
let me = self.me.lock().unwrap().as_ref().unwrap().clone(); | |
// We use `set_timeout` with a timeout of 0 in order to schedule the closure to be executed | |
// immediately after we return. | |
set_timeout(move || { | |
// Calling `poll_future_notify` will poll the future to see whether it's ready. | |
// If the future is not ready, we are guaranteed that the task (which is `me` here, and is | |
// also the same as `future_task` that we created at the start) is saved somewhere, and | |
// `notify` will be called on it later. | |
let val = task.lock().unwrap().poll_future_notify(&me, 0); | |
match val { | |
Ok(Async::Ready(item)) => println!("finished: {:?}", item), // You decide what to do here | |
Ok(Async::NotReady) => (), | |
Err(err) => panic!("error: {:?}", err), // You decide what to do here | |
} | |
}, 0); | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment