callback
callback() 函数将 JavaScript 函数封装为 C 函数指针,可以传递给 C 库作为回调。
导入
ts
import { callback, types } from 'senri_ffi';基本用法
ts
const cb = callback(
types.int32, // 返回值类型
[types.int32, types.int32], // 参数类型列表
(a, b) => a + b // JavaScript 函数
);
// cb 是一个 Pointer 实例,可以传给 C 函数签名
ts
function callback(
retType: any, // 返回值类型
argTypes: any[], // 参数类型列表
jsFn: Function, // JavaScript 回调函数
options?: any // 可选配置
): Pointer参数:
| 参数 | 类型 | 说明 |
|---|---|---|
retType | 类型描述符 | 回调的返回值类型 |
argTypes | 类型描述符数组 | 回调的参数类型列表 |
jsFn | Function | JavaScript 函数 |
options | object? | 可选配置(运行时特定) |
返回值: Pointer 实例,其 address 属性指向 C 回调的函数指针
自动垃圾回收
SenRi FFI 使用 FinalizationRegistry 自动管理回调生命周期:
ts
// 回调创建后注册到 FinalizationRegistry
const cb = callback(types.int32, [types.int32, types.int32], (a, b) => a + b);
// 当 cb 被垃圾回收时,FinalizationRegistry 自动调用 adapter.releaseCallback()
// 无需手动释放各运行时实现:
| 运行时 | 回调创建 | 回调释放 |
|---|---|---|
| KossJS | _senri_ffi.createCallback(retType, argTypes, fn) | _senri_ffi.free(ptr) |
| Bun | Bun.ffi.Callback({ returns, arguments }, fn) | 置空 __cb 引用 |
| Node.js | koffi.callback(retType, argTypes, fn) | 置空 __cb 引用 |
示例
传递给 C 函数 — qsort
ts
import { Library, callback, types, alloc } from 'senri_ffi';
const libc = Library.load(
process.platform === 'win32' ? 'msvcrt.dll' : 'libc.so.6'
);
// 定义比较回调
const compare = callback(
types.int32,
[pointer(types.int32), pointer(types.int32)],
(a, b) => {
const va = a.readInt32(0);
const vb = b.readInt32(0);
return va - vb;
}
);
// 准备数据
const arr = alloc(16);
arr.writeInt32(0, 3);
arr.writeInt32(4, 1);
arr.writeInt32(8, 4);
arr.writeInt32(12, 2);
// 调用 qsort
const qsort = libc.func('qsort', types.void, [
types.pointer, // 数组指针
types.uint64, // 元素个数
types.uint64, // 元素大小
types.pointer, // 比较函数指针
]);
qsort(arr.address, 4, 4, compare.address);
// 读取排序结果
console.log(arr.readInt32(0)); // 1
console.log(arr.readInt32(4)); // 2
console.log(arr.readInt32(8)); // 3
console.log(arr.readInt32(12)); // 4
libc.close();事件回调
ts
import { callback, types, Library } from 'senri_ffi';
const lib = Library.load('my_event_lib.so');
// 创建事件处理回调
const handler = callback(
types.void,
[types.int32, types.cstring],
(eventId, message) => {
console.log(`事件 ${eventId}: ${message}`);
}
);
// 注册回调
const register = lib.func('register_handler', types.void, [types.pointer]);
register(handler.address);
// handler 会在被垃圾回收时自动释放
// 如果需要保持回调存活,保留对 handler 的引用
globalThis._myHandler = handler;注意事项
保持引用
由于 FinalizationRegistry 会在回调对象被垃圾回收时自动释放底层的 C 回调,如果你的回调需要长期存活,务必保持对其的引用:
ts
// 正确:保持引用
globalThis._persistentCallback = callback(
types.void, [types.int32],
(x) => console.log(x)
);
// 错误:回调可能随时被 GC
someLib.registerCallback(
callback(types.void, [types.int32], (x) => console.log(x)).address
);错误处理
- 如果适配器未初始化(模块未正常加载),抛出
FFIError('Adapter not initialized') - 如果
jsFn不是函数,抛出FFIError('callback requires a function')
回调中的异常
JavaScript 回调函数中抛出的异常由各运行时处理,行为可能不同。建议在回调中自行捕获异常。
