neon/event/
mod.rs

1//! Exposes the JavaScript event loop for scheduling asynchronous events.
2//!
3//! ## The Event Loop
4//!
5//! The [_event loop_][event-loop] is how Node.js provides JavaScript programs
6//! access to concurrent events such as completion of [file][fs] or
7//! [network][net] operations, notification of scheduled [timers][timer], or
8//! receiving of messages from other [processes][process].
9//!
10//! When an asynchronous operation is started from JavaScript, it registers
11//! a JavaScript callback function to wait for the operation to complete. When
12//! the operation completes, the callback and the result data are added to an
13//! internal _event queue_ in the Node.js runtime so that the event can be
14//! processed in order.
15//!
16//! The event loop processes completed events one at a time in the JavaScript
17//! execution thread by calling the registered callback function with its result
18//! value as an argument.
19//!
20//! ## Creating Custom Events
21//!
22//! This module allows Neon programs to create new types of concurrent events
23//! in Rust and expose them to JavaScript as asynchronous functions.
24//!
25//! A common use for custom events is to run expensive or long-lived
26//! computations in a background thread without blocking the JavaScript
27//! thread. For example, using the [`psd` crate][psd-crate], a Neon program could
28//! asynchronously parse (potentially large) [PSD files][psd-file] in a
29//! background thread:
30//!
31//! ```
32//! # use neon::prelude::*;
33//! # #[cfg(not(feature = "napi-6"))]
34//! # type Channel = ();
35//! # fn parse(filename: String, callback: Root<JsFunction>, channel: Channel) { }
36//! # #[cfg(feature = "napi-6")]
37//! fn parse_async(mut cx: FunctionContext) -> JsResult<JsUndefined> {
38//!     // The types `String`, `Root<JsFunction>`, and `Channel` can all be
39//!     // sent across threads.
40//!     let filename = cx.argument::<JsString>(0)?.value(&mut cx);
41//!     let callback = cx.argument::<JsFunction>(1)?.root(&mut cx);
42//!     let channel = cx.channel();
43//!
44//!     // Spawn a background thread to complete the execution. The background
45//!     // execution will _not_ block the JavaScript event loop.
46//!     std::thread::spawn(move || {
47//!         // Do the heavy lifting inside the background thread.
48//!         parse(filename, callback, channel);
49//!     });
50//!
51//!     Ok(cx.undefined())
52//! }
53//! ```
54//!
55//! (Note that this usage of [`spawn`](std::thread::spawn) makes use of Rust's
56//! [`move`][move] syntax to transfer ownership of data to the background
57//! thread.)
58//!
59//! Upon completion of its task, the background thread can use the JavaScript
60//! callback and the channel to notify the main thread of the result:
61//!
62//! ```
63//! # use neon::prelude::*;
64//! # #[cfg(not(feature = "napi-6"))]
65//! # type Channel = ();
66//! # use psd::Psd;
67//! # use anyhow::{Context as _, Result};
68//! #
69//! fn psd_from_filename(filename: String) -> Result<Psd> {
70//!     Psd::from_bytes(&std::fs::read(&filename)?).context("invalid psd file")
71//! }
72//!
73//! # #[cfg(feature = "napi-6")]
74//! fn parse(filename: String, callback: Root<JsFunction>, channel: Channel) {
75//!     let result = psd_from_filename(filename);
76//!
77//!     // Send a closure as a task to be executed by the JavaScript event
78//!     // loop. This _will_ block the event loop while executing.
79//!     channel.send(move |mut cx| {
80//!         let callback = callback.into_inner(&mut cx);
81//!
82//!         match result {
83//!             Ok(psd) => {
84//!                 // Extract data from the parsed file.
85//!                 let obj = cx.empty_object()
86//!                     .prop(&mut cx, "width").set(psd.width())?
87//!                     .prop("height").set(psd.height())?
88//!                     .this();
89//!
90//!                 callback
91//!                     .bind(&mut cx)
92//!                     .args(((), obj))?
93//!                     .exec()?;
94//!             }
95//!             Err(err) => {
96//!                 use neon::types::extract::Error;
97//!                 callback
98//!                     .bind(&mut cx)
99//!                     .arg(Error::from(err))?
100//!                     .exec()?;
101//!             }
102//!         }
103//!
104//!         Ok(())
105//!     });
106//! }
107//! ```
108//!
109//! ## See also
110//!
111//! 1. Panu Pitkamaki. [Event loop from 10,000ft][event-loop].
112//!
113//! [event-loop]: https://bytearcher.com/articles/event-loop-10-000ft/
114//! [fs]: https://nodejs.org/dist/latest/docs/api/fs.html
115//! [net]: https://nodejs.org/dist/latest/docs/api/net.html
116//! [process]: https://nodejs.org/dist/latest/docs/api/process.html
117//! [timer]: https://nodejs.org/dist/latest/docs/api/timers.html
118//! [move]: https://doc.rust-lang.org/std/keyword.move.html
119//! [psd-crate]: https://crates.io/crates/psd
120//! [psd-file]: https://www.adobe.com/devnet-apps/photoshop/fileformatashtml/
121
122#[cfg(feature = "napi-4")]
123mod channel;
124
125mod task;
126
127pub use self::task::TaskBuilder;
128
129#[cfg(all(feature = "napi-5", feature = "futures"))]
130pub(crate) use self::channel::SendThrow;
131#[cfg(feature = "napi-4")]
132pub use self::channel::{Channel, JoinError, JoinHandle, SendError};
133
134#[cfg(feature = "napi-4")]
135#[deprecated(since = "0.9.0", note = "Please use the Channel type instead")]
136#[doc(hidden)]
137pub type EventQueue = self::channel::Channel;
138
139#[cfg(feature = "napi-4")]
140#[deprecated(since = "0.9.0", note = "Please use the SendError type instead")]
141#[doc(hidden)]
142pub type EventQueueError = self::channel::SendError;