跳转到主要内容
JSCallback 包装 ArkTS 函数,使其可作为 C 函数指针使用。

基本用法

const cb = new JSCallback(
  (a: number, b: number): number => a + b,
  { args: [FFIType.int32, FFIType.int32], returns: FFIType.int32 },
);

cb.call(2, 3); // → 5

闭包捕获

function makeAdder(x: number): JSCallback {
  return new JSCallback(
    (y: number): number => x + y,
    { args: [FFIType.int32], returns: FFIType.int32 },
  );
}

const add5 = makeAdder(5);
add5.call(3); // → 8
add5.close();

线程安全

可能从不同线程调用时设置 threadsafe: true
const cb = new JSCallback(
  (x: number): number => x * 2,
  { args: [FFIType.int32], returns: FFIType.double, threadsafe: true },
);

cb.threadsafe; // → true
cb.call(5);    // → 10
cb.close();

线程安全与 C 函数指针

threadsafe: true 时,cb.ptr 返回一个真实的 C 函数指针(蹦床地址),可直接传给原生 C 代码:
const cb = new JSCallback((x: number): number => x * 3, {
  args: [FFIType.int32],
  returns: FFIType.int32,
  threadsafe: true,
});

// cb.ptr 是真实函数地址,可通过 CFunction 包装后调用
const fn = CFunction({ args: [FFIType.int32], returns: FFIType.int32, ptr: cb.ptr });
fn(7); // → 21
fn.close();
cb.close();
内部机制:JSCallback 创建时通过 napi_create_threadsafe_function 创建 TSFN,同时分配 16 个预编译的本机蹦床函数,每个槽位对应一个独立函数地址。cb.ptr 通过 getCallbackPtr 返回该地址,原生 C 代码调用该地址时会先通过 TrampolineDispatcher 同步调用 ArkTS 回调并返回结果。

清理

始终调用 close() 释放资源:
cb.close();
cb.call(1); // 抛出异常:句柄无效

多个回调

import { JSCallback, FFIType } from 'arkffi';

const add = new JSCallback(fn1, { args: [FFIType.int32, FFIType.int32], returns: FFIType.int32 });
const mul = new JSCallback(fn2, { args: [FFIType.int32, FFIType.int32], returns: FFIType.int32 });

add.call(3, 4); // 7
mul.call(3, 4); // 12

add.close();
mul.close();

传递给原生函数

// C: void foreach(int* arr, int len, void (*cb)(int));
const cb = new JSCallback(
  (x: number): number => x * 2,
  { args: [FFIType.int32], returns: FFIType.int32 },
);

ffi.callMixed(handle, 'foreach', 'ipi', 'v', [arrPtr, len, cb.ptr], []);
cb.close();