Skip to content

Instantly share code, notes, and snippets.

@tomaka
Last active December 7, 2022 06:53
Show Gist options
  • Star 21 You must be signed in to star a gist
  • Fork 1 You must be signed in to fork a gist
  • Save tomaka/25e0f01c427a1bce0e0ddb10d278ead9 to your computer and use it in GitHub Desktop.
Save tomaka/25e0f01c427a1bce0e0ddb10d278ead9 to your computer and use it in GitHub Desktop.
// 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