neon/event/mod.rs
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142
//! Exposes the JavaScript event loop for scheduling asynchronous events.
//!
//! ## The Event Loop
//!
//! The [_event loop_][event-loop] is how Node.js provides JavaScript programs
//! access to concurrent events such as completion of [file][fs] or
//! [network][net] operations, notification of scheduled [timers][timer], or
//! receiving of messages from other [processes][process].
//!
//! 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][psd-crate], a Neon program could
//! asynchronously parse (potentially large) [PSD files][psd-file] in a
//! background thread:
//!
//! ```
//! # use neon::prelude::*;
//! # #[cfg(not(feature = "napi-6"))]
//! # type Channel = ();
//! # fn parse(filename: String, callback: Root<JsFunction>, channel: Channel) { }
//! # #[cfg(feature = "napi-6")]
//! 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`](std::thread::spawn) makes use of Rust's
//! [`move`][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:
//!
//! ```
//! # use neon::prelude::*;
//! # #[cfg(not(feature = "napi-6"))]
//! # type Channel = ();
//! # use psd::Psd;
//! # use anyhow::{Context as _, Result};
//! #
//! fn psd_from_filename(filename: String) -> Result<Psd> {
//! Psd::from_bytes(&std::fs::read(&filename)?).context("invalid psd file")
//! }
//!
//! # #[cfg(feature = "napi-6")]
//! 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
//!
//! 1. Panu Pitkamaki. [Event loop from 10,000ft][event-loop].
//!
//! [event-loop]: https://bytearcher.com/articles/event-loop-10-000ft/
//! [fs]: https://nodejs.org/dist/latest/docs/api/fs.html
//! [net]: https://nodejs.org/dist/latest/docs/api/net.html
//! [process]: https://nodejs.org/dist/latest/docs/api/process.html
//! [timer]: https://nodejs.org/dist/latest/docs/api/timers.html
//! [move]: https://doc.rust-lang.org/std/keyword.move.html
//! [psd-crate]: https://crates.io/crates/psd
//! [psd-file]: https://www.adobe.com/devnet-apps/photoshop/fileformatashtml/
#[cfg(feature = "napi-4")]
mod channel;
mod task;
pub use self::task::TaskBuilder;
#[cfg(all(feature = "napi-5", feature = "futures"))]
pub(crate) use self::channel::SendThrow;
#[cfg(feature = "napi-4")]
pub use self::channel::{Channel, JoinError, JoinHandle, SendError};
#[cfg(feature = "napi-4")]
#[deprecated(since = "0.9.0", note = "Please use the Channel type instead")]
#[doc(hidden)]
pub type EventQueue = self::channel::Channel;
#[cfg(feature = "napi-4")]
#[deprecated(since = "0.9.0", note = "Please use the SendError type instead")]
#[doc(hidden)]
pub type EventQueueError = self::channel::SendError;