# Calling and Fan-Out
source: https://docs.chalk.ai/docs/functions/calling-and-fan-out

## Call deployed Chalk Functions with defer() and get(), fan out with gather(), compose nested calls, and handle errors.

This page covers deferred calls, fan-out, and nested calls to a deployed Chalk Function, and how its errors reach the caller.

### Deferred calls with defer() and get()

.remote(...) blocks for the entire round trip. Use
.defer(...) instead when you want to submit a call and collect its result
later; it enqueues the call and returns a FunctionCallHandle immediately,
without waiting for the function to run. This example calls the add function from
Defining a function:

```
import chalkcompute

add = chalkcompute.RemoteFunction.from_name("add")

handle = add.defer(3, 5)
# ...do other work while the call runs...
result = handle.get()
print(result)  # 8
```

handle.get(timeout=None) blocks until the result is ready and returns the
same value .remote(...) would have returned.

Each handle exposes the server-assigned call_id it was enqueued under:

```
handle = add.defer(3, 5)
print(handle.call_id)
```

### Fan-out and gather()

Because .defer(...) returns immediately, you can enqueue many calls up
front and collect their results afterward. Use chalkcompute.gather(...) to
wait for a batch of handles concurrently and get their results back in order:

```
import chalkcompute

handles = [classify.defer(text) for text in texts]
results = chalkcompute.gather(handles)
```

gather(...) waits for the handles concurrently rather than one at a time,
so it returns as soon as the slowest call finishes. Pass timeout to bound the whole wait:

```
results = chalkcompute.gather(handles, timeout=30.0)
```

Inside a running event loop, gather(...) returns an awaitable instead of
blocking: await chalkcompute.gather(handles).

Each deferred call is still subject to the function's own
concurrency and rate limit
policies, so fanning out this way doesn't bypass those caps.

### Timeouts

Pass timeout (seconds) to bound how long get() waits:

```
try:
    result = handle.get(timeout=5.0)
except TimeoutError:
    print("still running")
```

A timeout only stops waiting locally. It does not cancel the underlying
call, which keeps running and can still be collected with another get().

### Errors from deferred calls

If the function raised an exception, .get() raises
chalkcompute.FunctionError describing the failure. Transient
infrastructure failures, such as a dropped connection between the queue and
the worker, are retried automatically before get() ever sees them; only
failures from the function body itself, or infra failures that exhaust
retries, are raised to the caller.

### Nested function calls

A function can call other functions with .remote(...). Each inner call is
dispatched independently, so you can compose pipelines where each step runs on
its own infrastructure. This example calls the transcribe async generator from
Streaming results:

```
import chalkcompute

@chalkcompute.function
def translate(text: str, target_lang: str) -> str:
    from transformers import pipeline
    translator = pipeline("translation", model=f"Helsinki-NLP/opus-mt-en-{target_lang}")
    return translator(text)[0]["translation_text"]


@chalkcompute.function
async def transcribe_and_translate(audio_uri: str, target_lang: str):
    """Transcribe audio, then translate each segment."""
    translate.wait_ready()

    async for segment in transcribe.remote(audio_uri):
        translated = await translate.remote(segment["text"], target_lang)
        yield {
            **segment,
            "translated": translated,
        }
```

The caller sees a single stream of translated segments:

```
import asyncio

transcribe_and_translate.wait_ready()

async def main():
    async for seg in transcribe_and_translate.remote(
        "https://example.com/meeting.wav",
        target_lang="es",
    ):
        print(f"[{seg['start']:.1f}s] {seg['translated']}")

asyncio.run(main())
```

```
[0.0s]  Bienvenidos a la revisión trimestral.
[3.2s]  Comencemos con las cifras de ingresos.
[6.1s]  El tercer trimestre llegó a 4,2 millones, un 18 por ciento más.
```

The outer function composes two inner calls: transcribe (async generator)
and translate (single-value). Each runs on its own compute configuration.

### Error handling

If a function raises an exception, the caller receives it as a RuntimeError with the
original exception type and message:

```
import chalkcompute

@chalkcompute.function
def divide(a: float, b: float) -> float:
    return a / b

divide.wait_ready()

try:
    divide.remote(1.0, 0.0)
except RuntimeError as e:
    print(e)  # ZeroDivisionError: division by zero
```

For async generators, an exception mid-stream terminates the iterator. Any segments
already yielded are still available to the caller; only subsequent iteration raises:

```
import chalkcompute

@chalkcompute.function
async def fragile_gen():
    yield "ok-1"
    yield "ok-2"
    raise ValueError("something went wrong")

import asyncio

async def main():
    async for item in fragile_gen.remote():
        print(item)

asyncio.run(main())
# ok-1
# ok-2
# RuntimeError: ValueError: something went wrong
```





