-
Notifications
You must be signed in to change notification settings - Fork 288
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Better onboarding docs #560
Comments
I'm new to rust, but one particular realm which is unclear to me from following the current examples is how to mix state between rust and node. There's a pattern across the examples of creating classes and functions followed by exporting them.
So that (and the equivalent for classes) show me quite well how to write some rust logic, convert my type into something javascript understands, and return it so that I can call the logic from javascript. But that's just logic, and for me it is unclear how to deal with state in general unless the state is entirely in javascript. For example, I would love to know how to do something like this: fn add_javascript_object_to_rust_collection(mut cx: FunctionContext, &mut state: State) {
let object = /* get it from the function context */
state.objects.push(object); // this is a RUST collection
}
register_module!(mut cx, {
// example of some state on the rust-side of the application
let mut state = State {
objects: Vec::new()
};
// seems logical but not valid, this is the hard part
cx.export_function("add", |cx| add_javascript_object_to_rust_collection(cx, &mut state))?;
// stuff like this works though
cx.export_function("foo", foo);
Ok(())
}); How does one have a function that uses state from js and rust at the same time? Writing the function body of A few other things that I found myself wondering while trying to learn neon:
|
@timetocode Did you figure out how to do state inside of neon modules? And I would be interested in the thread question as well. Can I just spin up Tokio and spawn some tasks without problems for example? |
@sirwindfield With the legacy backend, classes can be used to maintain state. With the N-API backend, Both of those use the JavaScript garbage collector to manage lifecycle and the state is passed back and forth across FFI as opaque pointers. Global state is also an option. For example, for Included below is a simple example of using use std::sync::atomic::{AtomicUsize, Ordering};
use std::sync::Arc;
use neon::prelude::*;
use once_cell::sync::Lazy;
use tokio::runtime::Runtime;
static RUNTIME: Lazy<Runtime> = Lazy::new(|| Runtime::new().unwrap());
struct Counter {
count: AtomicUsize,
queue: EventQueue,
}
impl Finalize for Counter {}
impl Counter {
fn new(queue: EventQueue) -> Arc<Self> {
Arc::new(Self {
count: AtomicUsize::new(0),
queue,
})
}
async fn fetch_add(&self, val: usize, order: Ordering) -> usize {
// Sleep for demonstration purposes only.
tokio::time::sleep(std::time::Duration::from_millis(100)).await;
self.count.fetch_add(val, order)
}
}
fn counter_new(mut cx: FunctionContext) -> JsResult<JsBox<Arc<Counter>>> {
let queue = cx.queue();
let counter = Counter::new(queue);
Ok(cx.boxed(counter))
}
fn counter_incr(mut cx: FunctionContext) -> JsResult<JsUndefined> {
let counter = Arc::clone(&&cx.argument::<JsBox<Arc<Counter>>>(0)?);
let cb = cx.argument::<JsFunction>(1)?.root(&mut cx);
RUNTIME.spawn(async move {
let result = counter.fetch_add(1, Ordering::SeqCst).await;
counter.queue.send(move |mut cx| {
let cb = cb.into_inner(&mut cx);
let this = cx.undefined();
let args = vec![
cx.undefined().upcast::<JsValue>(),
cx.number(result as f64).upcast(),
];
cb.call(&mut cx, this, args)?;
Ok(())
});
});
Ok(cx.undefined())
}
#[neon::main]
fn main(mut cx: ModuleContext) -> NeonResult<()> {
cx.export_function("counterNew", counter_new)?;
cx.export_function("counterIncr", counter_incr)?;
Ok(())
} "use strict";
const { promisify } = require("util");
const { counterNew, counterIncr } = require("./index.node");
const counterIncrAsync = promisify(counterIncr);
class Counter {
constructor() {
this.counter = counterNew();
}
async incr() {
return counterIncrAsync(this.counter);
}
}
async function run() {
const counter = new Counter();
console.log(await counter.incr());
console.log(await counter.incr());
const [a, b] = await Promise.all([counter.incr(), counter.incr()]);
console.log(a, b);
}
run().catch((err) => {
throw err;
}); There are a couple of interesting things in this code.
|
Update:
There's always more to do to improve docs, but since this issue isn't concretely scoped, I'm going to close it for now. We can open future issues for specific future docs goals. |
This is a placeholder for now, until we have a concrete plan.
We should take a fresh look at our onboarding experience, particularly at how we can make a smooth path for newcomers to go through the guides and feel productive as fast as possible.
Thoughts about different kinds of documentation for Neon:
This issue is for the third type, the guidebook.
Breaking down the work here, it should start with:
Value
andObject
JsBox
Send
andSync
The text was updated successfully, but these errors were encountered: