Skip to main content
JSCallback wraps TypeScript functions so they can be used as C function pointers.

Basic Usage

import { JSCallback, FFIType } from 'arkffi';

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

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

Closure Capture

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();

Thread Safety

Set threadsafe: true for callbacks that may be invoked from a different thread:
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();

Thread-Safe C Function Pointers

When threadsafe: true, cb.ptr returns a real C function pointer (trampoline address) that can be passed directly to native C code:
const cb = new JSCallback((x: number): number => x * 3, {
  args: [FFIType.int32],
  returns: FFIType.int32,
  threadsafe: true,
});

const fn = CFunction({ args: [FFIType.int32], returns: FFIType.int32, ptr: cb.ptr });
fn(7); // → 21
fn.close();
cb.close();
Internally, napi_create_threadsafe_function creates a TSFN, while 16 pre-compiled native trampoline functions are allocated — one per callback slot. cb.ptr returns the trampoline address via getCallbackPtr. When native C code calls this address, TrampolineDispatcher synchronously invokes the TypeScript callback and returns the result.

Cleanup

Always call close() to release resources:
cb.close();
cb.call(1); // throws: invalid handle

Multiple Callbacks

const add = new JSCallback(fn1, { args: ['i', 'i'], returns: 'i' });
const mul = new JSCallback(fn2, { args: ['i', 'i'], returns: 'i' });

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

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

Passing to Native Functions

// 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();