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

# Concurrency

> Run independent tasks simultaneously with Laravel's Concurrency facade to reduce total execution time and improve application performance.

## What is concurrency?

When you run multiple independent tasks sequentially—such as fetching data from several external APIs or executing multiple database aggregations—the total time equals the sum of each individual task's time.

Running those tasks **concurrently** means the total time equals the duration of the single longest task. Laravel's `Concurrency` facade provides a simple API for this pattern.

<Info>
  The `Concurrency` facade is available in Laravel 13. The default `process` driver works without any additional packages.
</Info>

## How it works

Laravel achieves concurrency by serializing the closures you pass in, dispatching them to a hidden Artisan CLI command, and executing each in its own PHP child process. After each closure finishes, the result is serialized back to the parent process.

Three drivers are available:

| Driver    | Description                                                                         |
| --------- | ----------------------------------------------------------------------------------- |
| `process` | Default. Spawns child PHP processes. Works in both web requests and CLI.            |
| `fork`    | Requires `spatie/fork`. Forks the current process for better performance. CLI only. |
| `sync`    | Executes closures sequentially in the current process. Useful for testing.          |

## Running concurrent tasks

Pass an array of closures to `Concurrency::run()`. The return value is an array of each closure's result, in the same order:

```php theme={null}
use Illuminate\Support\Facades\Concurrency;
use Illuminate\Support\Facades\DB;

[$userCount, $orderCount] = Concurrency::run([
    fn () => DB::table('users')->count(),
    fn () => DB::table('orders')->count(),
]);
```

### Choosing a driver

Use the `driver()` method to select a specific driver for one call:

```php theme={null}
$results = Concurrency::driver('fork')->run([
    fn () => fetchFromApiA(),
    fn () => fetchFromApiB(),
]);
```

To change the default driver for the whole application, publish the concurrency configuration file and update the `default` option:

```shell theme={null}
php artisan config:publish concurrency
```

## The `fork` driver

The `fork` driver is faster than `process` because it forks the running PHP process instead of booting a fresh one. However, PHP does not support forking during web requests, so this driver only works in Artisan commands and queue workers.

Install the required package before using it:

```shell theme={null}
composer require spatie/fork
```

<Warning>
  Do not use the `fork` driver inside a web request. Use it only inside Artisan commands or queue workers.
</Warning>

## Deferring tasks with `defer()`

Use `Concurrency::defer()` when you want to run tasks concurrently after the HTTP response has been sent to the user. The closures are not executed when `defer()` is called—they run in the background after the response is returned.

```php theme={null}
use App\Services\Metrics;
use Illuminate\Support\Facades\Concurrency;

Concurrency::defer([
    fn () => Metrics::report('users'),
    fn () => Metrics::report('orders'),
]);
```

<Tip>
  `defer()` is ideal for fire-and-forget work that you do not want to make the user wait for, such as recording analytics or warming a cache.
</Tip>

## Practical examples

### Calling multiple external APIs at once

Without concurrency, three one-second API calls take three seconds total. With concurrency, they take roughly one second:

```php theme={null}
use Illuminate\Support\Facades\Concurrency;
use Illuminate\Support\Facades\Http;

// Sequential — ~3 seconds
public function dashboardSlow(): array
{
    $inventory = Http::get('https://api.example.com/inventory')->json();
    $sales     = Http::get('https://api.example.com/sales')->json();
    $shipping  = Http::get('https://api.example.com/shipping')->json();

    return compact('inventory', 'sales', 'shipping');
}

// Concurrent — ~1 second
public function dashboard(): array
{
    [$inventory, $sales, $shipping] = Concurrency::run([
        fn () => Http::get('https://api.example.com/inventory')->json(),
        fn () => Http::get('https://api.example.com/sales')->json(),
        fn () => Http::get('https://api.example.com/shipping')->json(),
    ]);

    return compact('inventory', 'sales', 'shipping');
}
```

### Running multiple database aggregations

```php theme={null}
use Illuminate\Support\Facades\Concurrency;
use Illuminate\Support\Facades\DB;

public function statistics(): array
{
    [$totalUsers, $activeUsers, $totalOrders, $revenue] = Concurrency::run([
        fn () => DB::table('users')->count(),
        fn () => DB::table('users')->where('active', true)->count(),
        fn () => DB::table('orders')->count(),
        fn () => DB::table('orders')->sum('total_amount'),
    ]);

    return compact('totalUsers', 'activeUsers', 'totalOrders', 'revenue');
}
```

<Tip>
  Each child process opened by the `process` driver establishes its own database connection. Watch your database's maximum connection limit when running many concurrent tasks.
</Tip>

## Testing

Use `Concurrency::fake()` in tests to switch to the `sync` driver, which executes closures sequentially without spawning child processes:

```php theme={null}
use Illuminate\Support\Facades\Concurrency;

public function test_dashboard_returns_correct_data(): void
{
    Concurrency::fake();

    $response = $this->get('/dashboard');

    $response->assertStatus(200);
}
```

You can also set `CONCURRENCY_DRIVER=sync` in `.env.testing` to apply this globally across all tests.

## Caveats

<AccordionGroup>
  <Accordion title="Closures must be serializable">
    Laravel serializes closures to pass them to child processes. You cannot capture objects that are not serializable (database connections, file handles, open resources). Acquire those resources inside the closure instead.

    ```php theme={null}
    // Bad: capturing an Eloquent model that may not serialize cleanly
    $user = User::find(1);
    Concurrency::run([
        fn () => $user->orders()->count(),
    ]);

    // Good: fetch data inside the closure
    $userId = 1;
    Concurrency::run([
        fn () => Order::where('user_id', $userId)->count(),
    ]);
    ```
  </Accordion>

  <Accordion title="Process driver overhead">
    Spawning a child PHP process takes time. If a task takes only a few milliseconds, the overhead of the `process` driver may outweigh the benefit. Concurrency shines for tasks that take 100 ms or more, such as HTTP requests or heavy database queries.
  </Accordion>

  <Accordion title="Exceptions in concurrent tasks">
    If a closure throws an exception, `run()` re-throws it to the caller. If you want other closures to continue when one fails, wrap the body in a `try/catch` and return a fallback value.

    ```php theme={null}
    Concurrency::run([
        function () {
            try {
                return Http::get('https://api.example.com/data')->json();
            } catch (\Exception $e) {
                return null;
            }
        },
    ]);
    ```
  </Accordion>
</AccordionGroup>

<Card title="Queues" icon="list-checks" href="/en/queues">
  Learn how to defer work to background jobs using Laravel queues.
</Card>


## Related topics

- [Concurrency](/en/packages/laravel-copilot-sdk/concurrency.md)
- [Upgrade guide: Laravel 11 to 12](/en/blog/upgrade-11-to-12.md)
- [April 2026 Laravel Updates](/en/blog/changelog/202604.md)
- [HTTP client](/en/http-client.md)
- [Guide to Building Apps with VOICEVOX Engine API](/en/packages/laravel-voicevox/app-guide.md)
