Example of spawning a Rust async task on the tokio thread pool and resolving a JavaScript Promise after it completes.
Note: This example uses a pre-release version of Neon.
Asynchronously fetch the release date for the currently running Node process from nodejs.org.
For optimum task scheduling, it is best to have a single Rust task executor (e.g., tokio runtime). To make the runtime singleton available to Neon functions, it is stored in a global using OnceCell
.
use once_cell::sync::OnceCell;
use tokio::runtime::Runtime;
static RUNTIME: OnceCell<Runtime> = OnceCell::new();
A small helper is provided to lazily initialize the runtime and throw an exception on failure.
fn runtime<'a, C: Context<'a>>(cx: &mut C) -> NeonResult<&'static Runtime> {
RUNTIME.get_or_try_init(|| Runtime::new().or_else(|err| cx.throw_error(err.to_string())))
}
Tasks may be spawned on the tokio runtime by using the RUNTIME
handle. Spawning a task does not block the current thread. Inside a task the await
keyword may be used and typical async Rust patterns may be used.
let rt = runtime(&mut cx)?;
rt.spawn(async move {
// Asynchronous Rust may used in here
});
When a task is spawned on the tokio runtime, it will be executed at a later time. JavaScript needs to be notified when the task completes.
- Neon
Channel
may be created for moving an operation from the tokio thread pool back to the JavaScript main thread. cx.promise()
creates aJsPromise
andDeferred
for signaling JavaScript.JsPromise
is synchronously returned and may be used withawait
in JavaScriptDeferred
is used to settle theJsPromise
from theChannel
callback.
let channel = cx.channel();
let (deferred, promise) = cx.promise();
rt.spawn(async move {
// Code here executes non-blocking on the tokio thread pool
deferred.settle_with(&channel, move |mut cx| {
// Code here executes blocking on the JavaScript main thread
Ok(cx.undefined())
});
});
Ok(promise)