> ## Documentation Index
> Fetch the complete documentation index at: https://docs.arkffi.hmbill.cn/llms.txt
> Use this file to discover all available pages before exploring further.

# 異步調用

> 在不阻塞 JS 線程的情況下調用原生 C 函數

## 同步 vs 異步

arkffi 默認的 C 函數調用是**同步**的——`lib.symbols.add(2.0, 3.0)` 會阻塞當前線程直到函數返回。

對於耗時操作（複雜計算、I/O），可以使用 **`callAsync`** 將調用卸載到 libuv 工作線程，返回一個 Promise。

## `ffi.callAsync`

```typescript theme={null}
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);
});
```

### 語法

```typescript theme={null}
function callAsync(
  handle: bigint,
  funcName: string,
  argTypes: string,
  returnType: string,
  numArgs: number[],
  strArgs: string[],
): Promise<number>;
```

### 參數

| 參數           | 類型         | 說明                             |
| ------------ | ---------- | ------------------------------ |
| `handle`     | `bigint`   | `ffi.load()` 返回的庫句柄            |
| `funcName`   | `string`   | 函數名（必須已通過 `defineFunction` 註冊） |
| `argTypes`   | `string`   | 參數類型編碼                         |
| `returnType` | `string`   | 返回類型編碼                         |
| `numArgs`    | `number[]` | 數值參數                           |
| `strArgs`    | `string[]` | 字符串參數                          |

## `AsyncCFunction`

`AsyncCFunction` 是 `CFunction` 的異步版本，簽名完全一致，但返回 `Promise<number>`。不需要通過 `defineFunction` 註冊，直接使用函數指針：

```typescript theme={null}
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);
```

### 對比

| 特性                    | `CFunction`  | `AsyncCFunction`      |
| --------------------- | ------------ | --------------------- |
| 返回值                   | `number`（同步） | `Promise<number>`（異步） |
| 阻塞主線程                 | ✅ 是          | ❌ 否                   |
| 是否需要 `defineFunction` | ❌ 不需要        | ❌ 不需要                 |
| 參數類型支持                | 全部           | 全部                    |
| 關閉方法                  | `.close()`   | `.close()`            |

## 與 `dlopen` 配合使用

```typescript theme={null}
const Complex = Struct({
  real: FFIType.double,
  imag: FFIType.double,
});
const LibFFT = dlopen(LIB_NAME_FFT, {
  complex_new: { args: [FFIType.double, FFIType.double], returns: Complex },
  complex_add: { args: [Complex, Complex], returns: Complex },
  complex_sub: { args: [Complex, Complex], returns: Complex },
  complex_mul: { args: [Complex, Complex], returns: Complex },
  fft: { args: [FFIType.ptr, FFIType.int32], returns: FFIType.void }
});

let a = Complex.create({ real: 4, imag: 1 });
let b = Complex.create({ real: 3, imag: 3 });
LibFFT.symbols.complex_mul.async(a, b)
  .then((mulPtr) => {
    let mulVal = Complex.fromPtr(mulPtr);
    console.log('complex_mul', mulVal.real, mulVal.imag);
  })
  .finally(() => {
    LibFFT.close();
  });
console.log('LibFFT.symbols.complex_mul.async');
```

## 實現原理

1. `callAsync` 在 JS 線程解析參數並創建 `napi_async_work`
2. 工作線程上執行 `DispatchCallRaw`——純 C 函數指針調用，不涉及 NAPI
3. 完成後在 JS 線程上 resolve Promise
4. 整個過程中 JS 主線程不被阻塞

## 限制

* 不支持字符串返回類型（`callString` 無異步版本）
* 回調函數（`JSCallback`）不能在異步工作線程中調用
* 必須先將函數通過 `defineFunction` 註冊
