-
Notifications
You must be signed in to change notification settings - Fork 287
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
Implement Futures RFC #874
Conversation
This should be fine IMO. Node 14 has had support for Node-API 6 since 14.0.0, and Node 12 is EOL at the end of next month. So it's not worth the extra complexity to support older versions of Node that are about to be EOL. |
@dherman This has been updated:
This introduces a side-effect that calling
|
8fa4f33
to
be994e8
Compare
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I really like where this API has landed. I left a few suggestions for clarity, because some of the code we a little hard for me to follow.
+ 'static, | ||
{ | ||
let then = self.get::<JsFunction, _, _>(cx, "then")?; | ||
let catch = self.get::<JsFunction, _, _>(cx, "catch")?; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Minor semantic proposal: since getting a property could trigger arbitrary JS side effects (since, for example, it could be a getter function), I think we should perform the side effects in this order:
- Extract the
.then
property - Exec the
then
function - Extract the
.catch
property - Exec the
catch
function
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I went with this order to avoid calling anything if either getter fails. This is to avoid being in a half successful state.
It's still possible to be in this state if calling .then
fails, but at least it's less likely.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
OK, I see where you're coming from. I don't think I'm quite convinced but I also think it's not worth blocking progress.
|
||
[dependencies.neon] | ||
version = "*" | ||
path = "../../crates/neon" | ||
features = ["napi-8"] | ||
features = ["futures", "napi-experimental"] |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This now means we only test one path through the codebase, when the runtime is implemented differently depending on whether this flag is enabled or disabled. We should probably run our tests both with and without the "futures"
flag. IMO it's fine if we do this in follow-up work, since our next release will be a beta so the risk is somewhat mitigated.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🚢
+ 'static, | ||
{ | ||
let then = self.get::<JsFunction, _, _>(cx, "then")?; | ||
let catch = self.get::<JsFunction, _, _>(cx, "catch")?; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
OK, I see where you're coming from. I don't think I'm quite convinced but I also think it's not worth blocking progress.
} | ||
} | ||
|
||
// Marker that a `Throw` occurred that can be sent across threads for use in `JoinError` |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Nice!
I will follow-up with a PR to tests since it will require some refactoring to use multiple Neon modules in the test suite. In the meantime, I tested with a small example locally: use neon::prelude::*;
fn lazy_add(mut cx: FunctionContext) -> JsResult<JsUndefined> {
let a = cx.argument::<JsFunction>(0)?.root(&mut cx);
let b = cx.argument::<JsFunction>(1)?.root(&mut cx);
let cb = cx.argument::<JsFunction>(2)?.root(&mut cx);
let channel = cx.channel();
std::thread::spawn(move || {
let (a, b, cb) = channel
.send(move |mut cx| {
let a = a.into_inner(&mut cx);
let b = b.into_inner(&mut cx);
let cb = cb.into_inner(&mut cx);
let a = a.call_with(&cx).apply::<JsNumber, _>(&mut cx)?;
let b = b.call_with(&cx).apply::<JsNumber, _>(&mut cx)?;
Ok((a.value(&mut cx), b.value(&mut cx), cb.root(&mut cx)))
})
.join()
.unwrap();
let c = a + b;
channel.send(move |mut cx| {
cb.into_inner(&mut cx)
.call_with(&cx)
.arg(cx.number(c))
.exec(&mut cx)?;
Ok(())
});
});
Ok(cx.undefined())
}
#[neon::main]
fn main(mut cx: ModuleContext) -> NeonResult<()> {
cx.export_function("lazyAdd", lazy_add)?;
Ok(())
} const addon = require(".");
addon.lazyAdd(() => 1, () => 2, console.log); |
Implements #872
Limitation
This makes use of
JsFunction::new
with a closure, which requires Node-API 5. This seemed like a reasonable limitation to simplify the implementation.However, it would also be possible by passing the state with a
JsBox
and usingFunction.prototype.bind
.