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
143
144
145
//! 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);
//!         let this = cx.undefined();
//!         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][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;