Expand description
Exposes the JavaScript event loop for scheduling asynchronous events.
§The Event Loop
The event loop is how Node.js provides JavaScript programs access to concurrent events such as completion of file or network operations, notification of scheduled timers, or receiving of messages from other processes.
When an asynchronous operation is started from JavaScript, it registers a JavaScript callback function to wait for the operation to complete. When the operation completes, the callback and the result data are added to an internal event queue in the Node.js runtime so that the event can be processed in order.
The event loop processes completed events one at a time in the JavaScript execution thread by calling the registered callback function with its result value as an argument.
§Creating Custom Events
This module allows Neon programs to create new types of concurrent events in Rust and expose them to JavaScript as asynchronous functions.
A common use for custom events is to run expensive or long-lived
computations in a background thread without blocking the JavaScript
thread. For example, using the psd
crate, a Neon program could
asynchronously parse (potentially large) PSD files in a
background thread:
fn parse_async(mut cx: FunctionContext) -> JsResult<JsUndefined> {
// The types `String`, `Root<JsFunction>`, and `Channel` can all be
// sent across threads.
let filename = cx.argument::<JsString>(0)?.value(&mut cx);
let callback = cx.argument::<JsFunction>(1)?.root(&mut cx);
let channel = cx.channel();
// Spawn a background thread to complete the execution. The background
// execution will _not_ block the JavaScript event loop.
std::thread::spawn(move || {
// Do the heavy lifting inside the background thread.
parse(filename, callback, channel);
});
Ok(cx.undefined())
}
(Note that this usage of spawn
makes use of Rust’s
move
syntax to transfer ownership of data to the background
thread.)
Upon completion of its task, the background thread can use the JavaScript callback and the channel to notify the main thread of the result:
fn psd_from_filename(filename: String) -> Result<Psd> {
Psd::from_bytes(&std::fs::read(&filename)?).context("invalid psd file")
}
fn parse(filename: String, callback: Root<JsFunction>, channel: Channel) {
let result = psd_from_filename(filename);
// Send a closure as a task to be executed by the JavaScript event
// loop. This _will_ block the event loop while executing.
channel.send(move |mut cx| {
let callback = callback.into_inner(&mut cx);
match result {
Ok(psd) => {
// Extract data from the parsed file.
let obj = cx.empty_object()
.prop(&mut cx, "width").set(psd.width())?
.prop("height").set(psd.height())?
.this();
callback
.bind(&mut cx)
.args(((), obj))?
.exec()?;
}
Err(err) => {
use neon::types::extract::Error;
callback
.bind(&mut cx)
.arg(Error::from(err))?
.exec()?;
}
}
Ok(())
});
}
§See also
- Panu Pitkamaki. Event loop from 10,000ft.
Structs§
- Channel for scheduling Rust closures to execute on the JavaScript main thread.
- Error returned by
JoinHandle::join
indicating the associated closure panicked or threw an exception. - An owned permission to join on the result of a closure sent to the JavaScript main thread with
Channel::send
. - Error indicating that a closure was unable to be scheduled to execute on the event loop.
- Node asynchronous task builder