Appearance
Calling JavaScript from Rust
Fresh 🌱Beyond returning values, Rust often needs to call back into JavaScript: invoke a callback, call a method on an object, construct a class, or read a global. The modern way to do this is the bind() builder, which converts Rust arguments to JS for you.
The bind() builder
bind() produces a builder where each .arg(...) adds a typed argument and a terminal method runs the call:
rust
use neon::prelude::*;
fn call_js_functions(mut cx: FunctionContext) -> JsResult<JsValue> {
// A function passed in as the first argument
let callback: Handle<JsFunction> = cx.argument(0)?;
// Call with arguments using bind()
let result: Handle<JsValue> = callback
.bind(&mut cx)
.arg("hello")?
.arg(42)?
.call()?;
// Call with an explicit `this`
let obj = cx.empty_object();
let method: Handle<JsFunction> = obj.prop(&mut cx, "someMethod").get()?;
method
.bind(&mut cx)
.this(obj)?
.arg("data")?
.call()?;
// Call as a constructor, like `new Array(10)`
let array_ctor: Handle<JsFunction> = cx.global("Array")?;
let new_array: Handle<JsObject> = array_ctor
.bind(&mut cx)
.arg(10)?
.construct()?;
// Call a global function
let parse_int: Handle<JsFunction> = cx.global("parseInt")?;
let num: f64 = parse_int
.bind(&mut cx)
.arg("42")?
.arg(10)? // radix
.call()?;
Ok(result)
}The terminal methods:
| Method | Equivalent JavaScript |
|---|---|
.call() | fn(args...) |
.this(x)?.call() | fn.call(x, args...) |
.construct() | new fn(args...) |
.exec() | call for side effects, ignore the return value |
Two builder styles
You may also see the older .call_with(&mut cx).arg(handle).apply(&mut cx)? and .construct_with(...) builders (covered in Functions). bind() is the newer, more ergonomic form because it accepts plain Rust values and converts them.
Holding JS values across threads: Root
A Handle is only valid within the current synchronous context. The moment you cross into another thread or an async callback, you need a Root - a persistent reference that keeps a JavaScript value alive and can be turned back into a handle later.
rust
use neon::prelude::*;
struct AsyncState {
callback: Root<JsFunction>,
data: Root<JsObject>,
}
fn setup_async(mut cx: FunctionContext) -> JsResult<JsUndefined> {
let callback = cx.argument::<JsFunction>(0)?;
let data = cx.argument::<JsObject>(1)?;
// Root the values so they survive past this function
let state = AsyncState {
callback: callback.root(&mut cx),
data: data.root(&mut cx),
};
let channel = cx.channel();
std::thread::spawn(move || {
// Back on the JS main thread, turn Roots into live handles
channel.send(move |mut cx| {
let callback = state.callback.into_inner(&mut cx);
let data = state.data.into_inner(&mut cx);
callback.bind(&mut cx).arg(data)?.exec()?;
Ok(())
});
});
Ok(cx.undefined())
}The pattern is always the same:
value.root(&mut cx)to persist it,- move the
Rootinto the thread/closure, root.into_inner(&mut cx)to get a handle back on the main thread (via aChannel),- then call or use it.
You cannot touch JS off the main thread
JavaScript values may only be created or called on the main thread. A background thread holds a Root and uses a Channel to schedule the actual JS work back on the main thread. See Async Tasks, Promises & Channels.