> ## Documentation Index
> Fetch the complete documentation index at: https://mintlify.com/iii-hq/sdk/llms.txt
> Use this file to discover all available pages before exploring further.

# Function Invocation

> Call functions across the III network

III provides multiple ways to invoke functions, both synchronously (awaiting results) and asynchronously (fire-and-forget).

## call()

Invoke a function and wait for the result.

```typescript theme={null}
const result = await iii.call<TInput, TOutput>(function_id, data, timeoutMs?)
```

<ParamField path="function_id" type="string" required>
  ID of the function to invoke (e.g., `'users::create'`)
</ParamField>

<ParamField path="data" type="TInput" required>
  Input data to pass to the function
</ParamField>

<ParamField path="timeoutMs" type="number">
  Optional timeout in milliseconds. Defaults to `invocationTimeoutMs` from `init()` options (120000ms / 2 minutes)
</ParamField>

<ResponseField name="result" type="TOutput">
  The function's return value
</ResponseField>

### Example: Basic Call

```typescript theme={null}
import { init } from 'iii-sdk'

const iii = init('ws://localhost:49199')

// Register a function
iii.registerFunction(
  { id: 'math::add' },
  async (data: { a: number; b: number }) => {
    return { sum: data.a + data.b }
  }
)

// Call the function
const result = await iii.call<
  { a: number; b: number },
  { sum: number }
>('math::add', { a: 5, b: 3 })

console.log(result.sum) // 8
```

### Example: With Timeout

```typescript theme={null}
try {
  // Set a 5-second timeout
  const result = await iii.call(
    'slow::function',
    { data: 'input' },
    5000
  )
  console.log('Result:', result)
} catch (error) {
  console.error('Invocation failed:', error.message)
  // "Invocation timeout after 5000ms: slow::function"
}
```

### Example: Type-Safe Calls

```typescript theme={null}
interface CreateUserInput {
  email: string
  name: string
}

interface CreateUserOutput {
  id: string
  email: string
  name: string
  created_at: string
}

const user = await iii.call<CreateUserInput, CreateUserOutput>(
  'users::create',
  {
    email: 'john@example.com',
    name: 'John Doe'
  }
)

// TypeScript knows the shape of `user`
console.log(user.id)
console.log(user.email)
```

## callVoid()

Invoke a function asynchronously without waiting for a response (fire-and-forget).

```typescript theme={null}
iii.callVoid<TInput>(function_id, data)
```

<ParamField path="function_id" type="string" required>
  ID of the function to invoke
</ParamField>

<ParamField path="data" type="TInput" required>
  Input data to pass to the function
</ParamField>

<Info>
  `callVoid()` returns immediately and does not wait for the function to complete. Use this for background tasks, logging, notifications, etc.
</Info>

### Example: Fire-and-Forget

```typescript theme={null}
// Send analytics event without waiting
iii.callVoid('analytics::track', {
  event: 'user_login',
  user_id: '123',
  timestamp: Date.now()
})

// Send notification asynchronously
iii.callVoid('notifications::send_email', {
  to: 'user@example.com',
  subject: 'Welcome!',
  body: 'Thanks for signing up'
})

console.log('Events sent!')
```

### Example: Background Processing

```typescript theme={null}
iii.registerFunction(
  { id: 'orders::create' },
  async (data: { items: string[]; user_id: string }) => {
    const order = {
      id: crypto.randomUUID(),
      items: data.items,
      user_id: data.user_id,
      created_at: new Date().toISOString()
    }
    
    // Save order synchronously
    await db.orders.create({ data: order })
    
    // Process fulfillment asynchronously
    iii.callVoid('fulfillment::process', { order_id: order.id })
    
    // Send confirmation email asynchronously
    iii.callVoid('email::send_confirmation', {
      email: data.user_id,
      order_id: order.id
    })
    
    return order
  }
)
```

## trigger() / triggerVoid()

Aliases for `call()` and `callVoid()` for backward compatibility:

```typescript theme={null}
// Same as call()
const result = await iii.trigger('function::id', data, timeout)

// Same as callVoid()
iii.triggerVoid('function::id', data)
```

## Error Handling

Function invocations can fail for various reasons. Always handle errors appropriately:

```typescript theme={null}
try {
  const result = await iii.call('users::get', { id: '123' })
  console.log('User:', result)
} catch (error) {
  if (error.message.includes('not found')) {
    console.error('User does not exist')
  } else if (error.message.includes('timeout')) {
    console.error('Request timed out')
  } else {
    console.error('Unexpected error:', error)
  }
}
```

### Common Error Messages

<AccordionGroup>
  <Accordion title="Invocation timeout">
    ```
    Invocation timeout after 120000ms: function::id
    ```

    The function didn't respond within the timeout period. Increase the timeout or optimize the function.
  </Accordion>

  <Accordion title="Function not found">
    ```
    Function not found
    ```

    No worker has registered the requested function. Check the function ID and ensure the worker is connected.
  </Accordion>

  <Accordion title="Invocation failed">
    ```
    invocation_failed: [error message]
    ```

    The function threw an error. Check the error message for details.
  </Accordion>

  <Accordion title="iii is shutting down">
    ```
    iii is shutting down
    ```

    The SDK is shutting down and all pending invocations are being rejected.
  </Accordion>
</AccordionGroup>

## Distributed Tracing

All invocations automatically propagate W3C trace context for distributed tracing:

```typescript theme={null}
import { getContext } from 'iii-sdk'

iii.registerFunction(
  { id: 'service_a::process' },
  async (data: { value: string }) => {
    const { logger, trace } = getContext()
    
    // Add custom span attributes
    trace?.setAttribute('input.value', data.value)
    
    logger.info('Processing in service A')
    
    // Call another function - trace context is propagated
    const result = await iii.call('service_b::transform', data)
    
    logger.info('Received result from service B')
    
    return result
  }
)

iii.registerFunction(
  { id: 'service_b::transform' },
  async (data: { value: string }) => {
    const { logger, trace } = getContext()
    
    // This function is part of the same trace
    logger.info('Transforming in service B')
    trace?.setAttribute('transform.type', 'uppercase')
    
    return { transformed: data.value.toUpperCase() }
  }
)
```

The trace context flows automatically:

1. Service A starts a span
2. Service A calls Service B
3. Service B receives the trace context and creates a child span
4. Both spans are linked in the distributed trace

## Channel Passing

Pass channel references in invocation data for streaming:

```typescript theme={null}
// Create a channel
const channel = await iii.createChannel()

// Pass channel reader to another function
const resultPromise = iii.call('processor::analyze', {
  data_source: channel.readerRef
})

// Write data to the channel
channel.writer.stream.write(Buffer.from('chunk 1'))
channel.writer.stream.write(Buffer.from('chunk 2'))
channel.writer.stream.end()

// Wait for processing to complete
const result = await resultPromise
```

See [Channels API](/nodejs/api/channels) for details.

## Performance Tips

<AccordionGroup>
  <Accordion title="Use callVoid() for non-critical operations">
    ```typescript theme={null}
    // Don't block on analytics
    iii.callVoid('analytics::track', event)

    // Don't wait for cache warming
    iii.callVoid('cache::warm', { keys })
    ```
  </Accordion>

  <Accordion title="Set appropriate timeouts">
    ```typescript theme={null}
    // Fast operation - short timeout
    await iii.call('cache::get', { key }, 1000)

    // Slow operation - longer timeout
    await iii.call('ml::predict', { data }, 60000)
    ```
  </Accordion>

  <Accordion title="Parallelize independent calls">
    ```typescript theme={null}
    // Sequential (slow)
    const user = await iii.call('users::get', { id })
    const orders = await iii.call('orders::list', { user_id: id })
    const preferences = await iii.call('preferences::get', { user_id: id })

    // Parallel (fast)
    const [user, orders, preferences] = await Promise.all([
      iii.call('users::get', { id }),
      iii.call('orders::list', { user_id: id }),
      iii.call('preferences::get', { user_id: id })
    ])
    ```
  </Accordion>

  <Accordion title="Handle errors gracefully">
    ```typescript theme={null}
    const results = await Promise.allSettled([
      iii.call('service1::fn', data),
      iii.call('service2::fn', data),
      iii.call('service3::fn', data)
    ])

    results.forEach((result, i) => {
      if (result.status === 'fulfilled') {
        console.log(`Service ${i + 1}:`, result.value)
      } else {
        console.error(`Service ${i + 1} failed:`, result.reason)
      }
    })
    ```
  </Accordion>
</AccordionGroup>

## Best Practices

<AccordionGroup>
  <Accordion title="Always specify types">
    ```typescript theme={null}
    // Good - type-safe
    const result = await iii.call<Input, Output>('fn::id', data)

    // Avoid - no type safety
    const result = await iii.call('fn::id', data)
    ```
  </Accordion>

  <Accordion title="Use meaningful function IDs">
    ```typescript theme={null}
    // Good
    await iii.call('users::create', data)
    await iii.call('orders::cancel', { id })

    // Avoid
    await iii.call('create', data)
    await iii.call('fn_42', { id })
    ```
  </Accordion>

  <Accordion title="Set reasonable timeouts">
    ```typescript theme={null}
    // Default timeout for normal operations
    const iii = init(url, { invocationTimeoutMs: 30000 })

    // Override for specific calls
    await iii.call('fast::operation', data, 5000)
    await iii.call('slow::operation', data, 120000)
    ```
  </Accordion>
</AccordionGroup>
