Skip to content

Commit ee65857

Browse files
committed
Update tokio fetch
1 parent 105045c commit ee65857

File tree

2 files changed

+28
-69
lines changed

2 files changed

+28
-69
lines changed

examples/tokio-fetch/README.md

Lines changed: 11 additions & 61 deletions
Original file line numberDiff line numberDiff line change
@@ -2,77 +2,27 @@
22

33
Example of spawning a Rust async task on the [tokio][tokio] thread pool and resolving a JavaScript [Promise][promise] after it completes.
44

5-
_**Note:** This example uses a pre-release version of Neon._
6-
75
[tokio]: https://tokio.rs
86
[promise]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise
97

108
## Methods
119

12-
#### `function nodeReleaseDate(): Promise<string>`
13-
14-
Asynchronously fetch the release date for the currently running Node process from nodejs.org.
15-
16-
## Design
17-
18-
### Executor
19-
20-
For optimum task scheduling, it is best to have a single Rust task executor (e.g., tokio runtime). To make the runtime singleton available to Neon functions, it is stored in a global using `OnceCell`.
21-
22-
```rust
23-
use once_cell::sync::OnceCell;
24-
use tokio::runtime::Runtime;
25-
26-
static RUNTIME: OnceCell<Runtime> = OnceCell::new();
27-
```
10+
### `function nodeReleaseDate(version: string): Promise<string>`
2811

29-
A small helper is provided to lazily initialize the runtime and throw an exception on failure.
12+
Asynchronously fetch the release date for a given Node release from nodejs.org.
3013

31-
```rust
32-
fn runtime<'a, C: Context<'a>>(cx: &mut C) -> NeonResult<&'static Runtime> {
33-
RUNTIME.get_or_try_init(|| Runtime::new().or_else(|err| cx.throw_error(err.to_string())))
34-
}
35-
```
14+
`nodeReleaseDate` is a Rust `async fn`. The argument uses [`TryFromJs`][tryfromjs] and return value uses [`TryIntoJs`][tryintojs] for ergonomic conversions.
3615

37-
### Spawning Tasks
16+
### `function currentNodeReleaseDate(): Promise<string>`
3817

39-
Tasks may be spawned on the tokio runtime by using the `RUNTIME` handle. Spawning a task does *not* block the current thread. Inside a task the `await` keyword may be used and typical async Rust patterns may be used.
40-
41-
```rust
42-
let rt = runtime(&mut cx)?;
43-
44-
rt.spawn(async move {
45-
// Asynchronous Rust may used in here
46-
});
47-
```
48-
49-
### Promises
50-
51-
When a task is spawned on the tokio runtime, it will be executed at a later time. JavaScript needs to be notified when the task completes.
52-
53-
* Neon [`Channel`][channel] may be created for moving an operation from the tokio thread pool back to the JavaScript main thread.
54-
* [`cx.promise()`][cx-promise] creates a [`JsPromise`][js-promise] and [`Deferred`][deferred] for signaling JavaScript.
55-
* [`JsPromise`][js-promise] is synchronously returned and may be used with `await` in JavaScript
56-
* [`Deferred`][deferred] is used to settle the [`JsPromise`][js-promise] from the [`Channel`][channel] callback.
57-
58-
```rust
59-
let channel = cx.channel();
60-
let (deferred, promise) = cx.promise();
61-
62-
rt.spawn(async move {
63-
// Code here executes non-blocking on the tokio thread pool
18+
Asynchronously fetch the release date for the currently running Node process from nodejs.org.
6419

65-
deferred.settle_with(&channel, move |mut cx| {
66-
// Code here executes blocking on the JavaScript main thread
20+
`currentNodeReleaseDate` needs to access the JavaScript VM synchronously in order to get the current Node version before spawning an async tokio task.
6721

68-
Ok(cx.undefined())
69-
});
70-
});
22+
Writing a _synchronous_ Rust `fn`, that returns a Rust future, allows synchronous setup code that uses [`Cx`][cx].
7123

72-
Ok(promise)
73-
```
24+
**Note**: The returned future is still required to be `Send + 'static`.
7425

75-
[channel]: https://docs.rs/neon/0.10.0-alpha.3/neon/event/struct.Channel.html
76-
[cx-promise]: https://docs.rs/neon/0.10.0-alpha.3/neon/context/trait.Context.html#method.promise
77-
[js-promise]: https://docs.rs/neon/0.10.0-alpha.3/neon/types/struct.JsPromise.html
78-
[deferred]: https://docs.rs/neon/0.10.0-alpha.3/neon/types/struct.Deferred.html
26+
[tryfromjs]: https://docs.rs/neon/latest/neon/types/extract/trait.TryFromJs.html
27+
[tryintojs]: https://docs.rs/neon/latest/neon/types/extract/trait.TryIntoJs.html
28+
[cx]: https://docs.rs/neon/latest/neon/context/struct.Cx.html

examples/tokio-fetch/src/lib.rs

Lines changed: 17 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -31,17 +31,26 @@ fn node_version(cx: &mut Cx) -> NeonResult<String> {
3131
cx.global::<JsObject>("process")?.prop(cx, "version").get()
3232
}
3333

34+
// Export an async JavaScript function where the body is executed on the tokio thread pool
35+
#[neon::export]
36+
async fn node_release_date(version: String) -> Result<String, Error> {
37+
let release = fetch_node_release(&version)
38+
.await?
39+
.ok_or_else(|| format!("Could not find version: {version}"))?;
40+
41+
Ok(release.date)
42+
}
43+
44+
// Similar to `node_release_date`, but includes some setup code synchronously executed
45+
// on the JavaScript main thread before return a task for tokio. Since this is not
46+
// an `async fn`, we need to explicitly tell the export macro that it returns a future.
3447
#[neon::export(async)]
35-
fn node_release_date(
48+
fn current_node_release_date(
3649
cx: &mut Cx,
3750
) -> NeonResult<impl Future<Output = Result<String, Error>> + use<>> {
51+
// Executes synchronously on the JavaScript main thread
3852
let version = node_version(cx)?;
3953

40-
Ok(async move {
41-
let release = fetch_node_release(&version)
42-
.await?
43-
.ok_or_else(|| format!("Could not find version: {version}"))?;
44-
45-
Ok(release.date)
46-
})
54+
// This task is executed asynchronously on the tokio thread pool
55+
Ok(node_release_date(version))
4756
}

0 commit comments

Comments
 (0)