Promise
Promise<T>
Awaiting a JavaScript Promise in Rust sounds crazy, but it's feasible in NAPI-RS.
The Promise<T> in NAPI-RS implements the std::future::Future trait, so you can use the await keyword to await it.
TIP
Awaiting a JavaScript Promise needs the async or tokio_rt feature.
tokio_rt enables napi4 for you.
INFO
Promise<T> is Send when T is Send, so the compiler prevents a
non-Send resolved value from crossing Tokio worker threads.
use napi::bindgen_prelude::*;
use napi_derive::napi;
#[napi]
pub async fn async_plus_100(p: Promise<u32>) -> Result<u32> {
let v = p.await?;
v.checked_add(100)
.ok_or_else(|| Error::new(Status::InvalidArg, "result exceeds u32"))
}
import { asyncPlus100 } from './index.js'
const fx = 20
const result = await asyncPlus100(
new Promise((resolve) => {
setTimeout(() => resolve(fx), 50)
}),
)
console.log(result) // 120
PromiseRaw<'env, T>
PromiseRaw<'env, T> represent the raw Promise value in the JavaScript, it contains the lifetime so it can only be used in the sync context.
But conveniently, it can call methods on the JavaScript Promise, such as then, catch, and finally.
use napi::bindgen_prelude::*;
use napi_derive::napi;
#[napi]
pub fn promise_callback(promise: PromiseRaw<u32>) -> Result<PromiseRaw<u32>> {
promise.then(|ctx| Ok(ctx.value + 100))
}
import { promiseCallback } from './index.js'
const value = await promiseCallback(Promise.resolve(100))
console.log(value) // 200
AsyncBlock<T>
AsyncBlock<T> is the other way to return a Promise from Rust. Where an exported async fn starts when JavaScript calls it and resolves with the function's return value, an AsyncBlock wraps a manually built future via AsyncBlockBuilder — which additionally lets you attach a dispose hook (.with_dispose) or a map closure (build_with_map) that runs on the JavaScript thread at resolution time, so the promise can resolve with values that can only be created there (a zero-copy BufferSlice<'static>, an instance of a JavaScript class, …). The future starts eagerly when the Rust function is called, not when the promise is awaited.
#[napi]
pub fn process_buffer(env: &Env, buffer: Buffer) -> Result<AsyncBlock<Buffer>> {
AsyncBlockBuilder::new(async move { Ok(buffer) }).build(env)
}
export declare function processBuffer(buffer: Buffer): Promise<Buffer>
See Web Streams for the full AsyncBlockBuilder API and worked examples, and TypedArray for more AsyncBlock usage with buffers.