import { snippets } from "@/lib/generated/snippets";
import { Snippet } from "@/components/code";
import { Callout, Tabs } from "nextra/components";
import UniversalTabs from "@/components/UniversalTabs";
import Keywords from "@/components/Keywords";

# Tasks

The fundamental unit of work in Hatchet is a **task**. At its most basic level, a task is just a function. You can invoke a task on its own (a "standalone" task), compose tasks into a [DAG workflow](/v1/directed-acyclic-graphs), or use [durable task composition](/v1/child-spawning) to spawn child tasks at runtime.

Every task you invoke is **durable** - Hatchet persists it, its state, and its results even after it finishes running.

## Defining a task

A task needs a name and a function. The function accepts an [input](#input-and-output) and a [context](#the-context-object).

<UniversalTabs items={["Python", "Typescript", "Go", "Ruby"]}>
  <Tabs.Tab title="Python">
    <Snippet
      src={snippets.python.quickstart.workflows.first_task.simple_task}
    />

    In the Python SDK, the arguments to the task are passed _positionally_, which means you can name them whatever you like. For instance, defining a task as `async def my_task(foo: Input, bar: Context) -> None:` is perfectly valid. Tasks in Hatchet can be either sync or async, although we generally recommend trying to use async wherever possible.

  </Tabs.Tab>
  <Tabs.Tab title="Typescript">
    <Snippet src={snippets.typescript.simple.workflow.declaring_a_task} />

    In the TypeScript SDK, the `fn` argument is a function that takes the workflow's input and a context object. The context object contains information about the workflow run (e.g. the run ID, the workflow's input, etc). It can be synchronous or asynchronous.

  </Tabs.Tab>
  <Tabs.Tab title="Go">
    <Snippet src={snippets.go.simple.main.declaring_a_task} />
  </Tabs.Tab>
  <Tabs.Tab title="Ruby">
    <Snippet src={snippets.ruby.quickstart.workflows.first_task.simple_task} />
  </Tabs.Tab>
</UniversalTabs>

## Input and output

Every task receives an **input** - a JSON-serializable object passed when the task is triggered. The value that is returned from the task becomes the task's **output**, which callers receive when they await the result of the task.

Hatchet's SDKs support type-checked and runtime-validated input and output types for tasks, so that you can integrate your Hatchet tasks into your codebase in a type-safe and predictable way that provides you all of the guarantees you get from, for example, replacing the Hatchet task run with a local function call.

You can refer to the [examples above](#defining-a-task) to see how to provide validators for task inputs and outputs.

## The context object

In addition to input and output payloads, every task receives a **context**. The context provides Hatchet-related information that might be useful to the execution of the task at runtime. For instance, you might access the workflow run ID, the task run ID, or the retry count from the context and have your task's application logic do something with those values.

The context also provides helper methods for interacting with a number of Hatchet's features, such as [managing cancellations](/v1/cancellation), [refreshing timeouts](/v1/timeouts#refreshing-timeouts), [pushing stream events](/v1/streaming#pushing-stream-events) and more.

<UniversalTabs items={["Python", "Typescript", "Go", "Ruby"]}>
  <Tabs.Tab title="Python">
    See the [Python SDK reference](/reference/python/context) for more details
  </Tabs.Tab>
  <Tabs.Tab title="Typescript">
    See the [TypeScript SDK reference](/reference/typescript/Context) for more
    details
  </Tabs.Tab>
  <Tabs.Tab title="Go">
    See the [Go SDK
    reference](https://pkg.go.dev/github.com/hatchet-dev/hatchet/sdks/go#Context)
    for more details
  </Tabs.Tab>
  <Tabs.Tab title="Ruby">
    Ruby SDK reference coming soon! For now, see the [Python SDK
    reference](/reference/python/context) to get a sense of what's available.
  </Tabs.Tab>
</UniversalTabs>

## Configuration

Tasks can be configured to handle common problems in distributed systems. For example, you might want to automatically retry a task when an external API returns a transient error, or limit how many instances of a task run at the same time to avoid overwhelming a downstream service.

| Concept                                                    | What it does                                               |
| ---------------------------------------------------------- | ---------------------------------------------------------- |
| [Retries](/v1/retry-policies)                              | Retry the task on failure, with optional backoff.          |
| [Timeouts](/v1/timeouts)                                   | Limit how long a task may wait to be scheduled or to run.  |
| [Concurrency](/v1/concurrency)                             | Distribute load fairly between your customers.             |
| [Rate limits](/v1/rate-limits)                             | Throttle task execution over a time window.                |
| [Priority](/v1/priority)                                   | Influence scheduling order relative to other queued tasks. |
| [Worker affinity](/v1/advanced-assignment/worker-affinity) | Prefer or require specific workers for this task.          |

## How tasks execute on workers

Tasks don't run on their own. [Workers](/v1/workers) execute them. A worker is a long-running process that registers one or more tasks with Hatchet. When you trigger a task, Hatchet places it in a queue and assigns it to an available worker that has registered that task. When a task completes, the Hatchet SDK running on the worker sends the result back to Hatchet, which marks the task as a success or failure, displays the results, and so on.

<Keywords keywords="task, workflow, input, output" />
