Reference
Ref
é muito similar a Rc
em Rust. No entanto, ele não diminuirá a contagem de referência quando descartado, você precisa chamar o método unref
manualmente.
Em alguns cenários, você pode precisar estender manualmente a vida útil de certos objetos JavaScript para evitar que sejam recolhidos pelo GC prematuramente. Ao contrário de usar objetos em JavaScript, manter uma referência ou valor de um JsObject
em Rust é opaco para o sistema de GC do Node.js, o que significa que o GC não sabe que você ainda tem um valor de objeto que deseja usar em algum momento futuro. O GC o reciclará impiedosamente quando achar adequado. Neste ponto, precisamos criar uma Reference para este objeto, o que equivale a dizer ao sistema de GC do Node.js: ei, eu ainda preciso deste objeto, não o recicle ainda.
Normalmente, precisamos estender manualmente a vida útil de um objeto ao realizar algumas operações assíncronas, como armazenar o valor de algum objeto em Task
ou ThreadSafeFunction
. Esses objetos não podem ser destruídos após a execução da função, então precisamos esperar até que a tarefa assíncrona termine de executar e, em seguida, chamar manualmente o método unref
para informar ao GC que não precisamos mais do objeto.
struct Hash(Ref<JsBufferValue>);
impl Task for Hash {
type Output = String;
type JsValue = JsString;
fn compute(&mut self) -> Result<Self::Output> {
Ok(base64(&self.0))
}
fn resolve(self, env: Env, output: Self::Output) -> Result<Self::JsValue> {
let result = env.create_string_from_std(output);
self.0.unref(env)?;
result
}
}
#[js_function(1)]
// return Promise Object
fn async_hash(ctx: CallContext) -> Result<JsObject> {
let input_data = ctx.get::<JsBuffer>(0)?.into_ref()?;
let hash = Hash(input_data);
ctx.env.spawn(hash).map(|async_task| async_task.promise_object())
}
fn base64(data: &[u8]) -> String {
todo!();
}
asyncHash(Buffer::from([1, 2])).then((result) => {
console.log(result) // 0102
})
Para objetos JavaScript que não sejam JsBuffer
, você pode usar Env::create_reference
para criar referências para eles e recuperar esses objetos JavaScript posteriormente com Env::get_reference_value
. Por exemplo:
struct CallbackContext {
callback: Ref<()>
}
#[napi]
pub fn wrap_in_obj(env: Env, js_fn: JsFunction) -> Result<JsObject> {
let mut js_obj = env.create_object()?;
// cria uma reference para a função javascript
let js_fn_ref = env.create_reference(js_fn)?;
let ctx = CallbackContext {
callback: js_fn_ref,
};
// faz o wrap em um object
env.wrap(&mut js_obj, ctx)?;
Ok(js_obj)
}
#[napi]
pub fn call_wrapped_fn(env: Env, js_obj: JsObject) -> Result<()> {
let ctx: &mut CallbackContext = env.unwrap(&js_obj)?;
let js_fn: JsFunction = env.get_reference_value(&ctx.callback)?;
// a função javascript não deve ser recuperada antes de chamarmos Ref::unref()
js_fn.call_without_args(None)?;
Ok(())
}
const logSomething = () => {
console.log('hello')
}
const obj = wrapInObj(logSomething)
callWrappedFn(obj) // log 'hello'
Você sempre deve obter objetos JavaScript de referências em vez de armazenar esses objetos JavaScript diretamente. JsValue
referenciados ainda podem se tornar inválidos após algum tempo.