Module neon::event[][src]

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 `EventQueue` 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 queue = cx.queue();

    // Spawn a thread to complete the execution. This will _not_ block the
    // JavaScript event loop.
    std::thread::spawn(move || {
        // Do the heavy lifting inside the background thread.
        parse(filename, callback, queue);
    });

    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 event queue to notify the main thread of the result:

fn psd_from_filename(filename: String) -> Result<Psd, Error> {
    Psd::from_bytes(&std::fs::read(&filename)?)
}

fn parse(filename: String, callback: Root<JsFunction>, queue: EventQueue) {
    let result = psd_from_filename(filename);

    // Send a closure as a task to be executed by the JavaScript event
    // queue. This _will_ block the event queue while executing.
    queue.send(move |mut cx| {
        let callback = callback.into_inner(&mut cx);
        let this = cx.undefined();
        let null = cx.null();
        let args = match result {
            Ok(psd) => {
                // Extract data from the parsed file.
                let width = cx.number(psd.width());
                let height = cx.number(psd.height());

                // Save the data in a result object.
                let obj = cx.empty_object();
                obj.set(&mut cx, "width", width)?;
                obj.set(&mut cx, "height", height)?;
                vec![
                    cx.null().upcast::<JsValue>(),
                    obj.upcast(),
                ]
            }
            Err(err) => {
                let err = cx.string(err.to_string());
                vec![
                    err.upcast::<JsValue>(),
                ]
            }
        };

        callback.call(&mut cx, this, args)?;

        Ok(())
    });
}

See also

  1. Panu Pitkamaki. Event loop from 10,000ft.

Structs

EventHandler