async fn
TIP
Você deve habilitar o recurso async ou tokio_rt no napi para usar async fn:
[dependencies]
napi = { version = "3", features = ["async", "tokio_fs", "tokio_time"] }
napi-derive = "3"
Os exemplos abaixo usam as subfeatures tokio_fs e tokio_time. Habilite somente
as APIs Tokio que o seu addon usa.
Integração com Tokio
Você pode realizar muitos trabalhos async/multi-threaded com AsyncTask e ThreadsafeFunction, mas às vezes você pode querer usar diretamente as crates do ecossistema async do Rust.
Com async ou tokio_rt habilitado, o NAPI-RS fornece um runtime Tokio. Se você
aguardar um future Tokio em uma async fn exportada, o NAPI-RS o executará
nesse runtime e converterá o resultado em uma Promise JavaScript.
use napi::bindgen_prelude::*;
use napi_derive::napi;
use napi::tokio::fs;
#[napi]
pub async fn read_file_async(path: String) -> Result<Buffer> {
let content = fs::read(path).await?;
Ok(content.into())
}
⬇️ ⬇️ ⬇️ ⬇️ ⬇️ ⬇️ ⬇️ ⬇️ ⬇️
export function readFileAsync(path: string): Promise<Buffer>
&mut self inseguro
Em alguns casos, você pode precisar usar &mut self em uma async fn. No entanto, isso é unsafe no NAPI-RS, porque o self também é de propriedade do runtime Node.js. Você não pode garantir que o self seja de propriedade apenas do Rust.
use napi_derive::napi;
#[napi]
pub struct Engine {}
#[napi]
impl Engine {
#[napi]
pub async fn run(&mut self) {}
}
error: &mut self in async napi methods should be marked as unsafe
--> src/lib.rs:9:18
|
9 | pub async fn run(&mut self) {}
| ^^^
Você precisa marcar a fn como unsafe para usar &mut self em uma async fn.
use napi_derive::napi;
#[napi]
pub struct Engine {}
#[napi]
impl Engine {
#[napi]
pub async unsafe fn run(&mut self) {}
}
Referência automática
Normalmente, os valores JavaScript só são válidos dentro de uma chamada de função. Com async fn isso não acontece — os valores JavaScript podem ser coletados pelo garbage collector em qualquer ponto de await.
INFO
Veja Entendendo lifetime para mais detalhes.
Há 3 tipos de parâmetros que são automaticamente transformados em tipos Reference:
&self&mut selfThis<T>
Considere o seguinte exemplo:
use napi::bindgen_prelude::*;
use napi_derive::napi;
#[napi]
pub struct NativeClass {
name: String,
}
#[napi]
impl NativeClass {
#[napi(constructor)]
pub fn new(name: String) -> Self {
Self { name }
}
#[napi]
pub async fn sleep(&self, delay: u32) -> Result<&str> {
napi::tokio::time::sleep(std::time::Duration::new(delay as u64, 0)).await;
Ok(&self.name)
}
}
const nativeClass = new NativeClass('Brooklyn')
const name = await nativeClass.sleep(1)
console.log(name) // Brooklyn
Há uma chamada implícita de napi_create_reference para o valor Object JavaScript que contém o NativeClass antes da chamada da async fn; e uma chamada implícita de napi_delete_reference após a chamada da async fn.
Essa estratégia garante que o NativeClass permaneça vivo durante a chamada da async fn.
Além do async fn: AsyncBlock
Uma async fn exportada cobre o caso comum, mas ela sempre resolve sua promise com o valor de retorno da função, convertido após a conclusão do future. Quando você precisa de mais controle — resolver com um valor que só pode ser criado na thread JavaScript (por exemplo, um BufferSlice<'static> zero-copy ou uma Response Web), ou executar uma limpeza por meio de um hook de dispose quando o future é concluído — retorne um AsyncBlock<T> de uma função #[napi] síncrona. O future inicia imediatamente (eagerly) no runtime do NAPI-RS e se converte em uma Promise JavaScript, assim como uma async fn.
Veja Web Streams: AsyncBlock para a API completa do AsyncBlockBuilder e exemplos.
Runtimes personalizados
Por padrão, o future é executado no runtime Tokio gerenciado pelo NAPI-RS. Com a feature Cargo async-runtime, você pode registrar seu próprio executor — incluindo runtimes sem tokio para WASI sem threads ou workerd — e toda async fn gerada será executada nele. Veja Runtime async personalizado.