跳轉到主要內容
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();