跳转到主要内容

同步 vs 异步

arkffi 默认的 C 函数调用是同步的——lib.symbols.add(2.0, 3.0) 会阻塞当前线程直到函数返回。 对于耗时操作(复杂计算、I/O),可以使用 callAsync 将调用卸载到 libuv 工作线程,返回一个 Promise。

ffi.callAsync

import ffi from 'liblibrary.so';

const handle = ffi.load('libffi_target.so');
ffi.defineFunction(handle, 'factorial', 'i', 'l');

ffi.callAsync(handle, 'factorial', 'i', 'l', [10], []).then((r: number) => {
  console.log(r); // 3628800
  ffi.close(handle);
});

语法

function callAsync(
  handle: bigint,
  funcName: string,
  argTypes: string,
  returnType: string,
  numArgs: number[],
  strArgs: string[],
): Promise<number>;

参数

参数类型说明
handlebigintffi.load() 返回的库句柄
funcNamestring函数名(必须已通过 defineFunction 注册)
argTypesstring参数类型编码
returnTypestring返回类型编码
numArgsnumber[]数值参数
strArgsstring[]字符串参数

AsyncCFunction

AsyncCFunctionCFunction 的异步版本,签名完全一致,但返回 Promise<number>。不需要通过 defineFunction 注册,直接使用函数指针:
import { AsyncCFunction, FFIType } from 'arkffi';
import ffi from 'liblibrary.so';

const handle = ffi.load('libffi_target.so');
const addPtr = ffi.getSymbolPtr(handle, 'add');

const asyncAdd = AsyncCFunction({
  args: [FFIType.double, FFIType.double],
  returns: FFIType.double,
  ptr: addPtr,
});

// 通过 .then 获取结果
asyncAdd(2.0, 3.0).then((r: number) => {
  console.log(r); // 5.0
});

// 多个调用并发执行
Promise.all([asyncAdd(1.0, 2.0), asyncAdd(3.0, 4.0)]).then((results) => {
  console.log(results); // [3.0, 7.0]
});

asyncAdd.close();
ffi.close(handle);

对比

特性CFunctionAsyncCFunction
返回值number(同步)Promise<number>(异步)
阻塞主线程✅ 是❌ 否
是否需要 defineFunction❌ 不需要❌ 不需要
参数类型支持全部全部
关闭方法.close().close()

dlopen 配合使用

函数必须通过 defineFunction 注册到注册表后才能异步调用:
const handle = ffi.load('libffi_target.so');
ffi.defineFunction(handle, 'add', 'dd', 'd');

const p1 = ffi.callAsync(handle, 'add', 'dd', 'd', [1.0, 2.0], []);
const p2 = ffi.callAsync(handle, 'add', 'dd', 'd', [3.0, 4.0], []);
Promise.all([p1, p2]).then(([r1, r2]) => {
  console.log(r1, r2);
  ffi.close(handle);
});

实现原理

  1. callAsync 在 JS 线程解析参数并创建 napi_async_work
  2. 工作线程上执行 DispatchCallRaw——纯 C 函数指针调用,不涉及 NAPI
  3. 完成后在 JS 线程上 resolve Promise
  4. 整个过程中 JS 主线程不被阻塞

限制

  • 不支持字符串返回类型(callString 无异步版本)
  • 回调函数(JSCallback)不能在异步工作线程中调用
  • 必须先将函数通过 defineFunction 注册