> ## 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

> Een uitleg van hoe je met de Concurrency-facade van Laravel 13 meerdere taken tegelijk uitvoert en zo de performance van je applicatie verbetert.

## Wat is concurrency?

Wanneer je meerdere taken die niet van elkaar afhankelijk zijn — zoals requests naar meerdere externe API's of database-aggregaties — **na elkaar uitvoert**, is de totale tijd de som van alle afzonderlijke verwerkingstijden.
Voer je ze **tegelijk uit**, dan wordt de totale tijd teruggebracht tot die van de langzaamste taak.

Laravels `Concurrency`-facade maakt zulke parallelle uitvoering mogelijk met een eenvoudige API.

<Info>
  De `Concurrency`-facade is geïntroduceerd in Laravel 11 en is ook in Laravel 13 nog beschikbaar.
  De standaarddriver gebruikt PHP-childprocessen en werkt dus zonder extra packages.
</Info>

## Hoe het werkt

De `Concurrency`-facade serialiseert de doorgegeven closures, stuurt ze naar een verborgen Artisan-commando en voert ze elk uit in een apart PHP-proces.
Zodra de verwerking klaar is, wordt de returnwaarde geserialiseerd en teruggestuurd naar het parent-proces.

Er zijn drie drivers beschikbaar.

| Driver    | Beschrijving                                                                                          |
| --------- | ----------------------------------------------------------------------------------------------------- |
| `process` | Standaard. Start PHP-childprocessen. Werkt ook binnen webrequests                                     |
| `fork`    | Vereist het `spatie/fork`-package. Forkt processen en werkt daarom alleen via de CLI, maar is sneller |
| `sync`    | Voert niet parallel maar sequentieel uit. Geschikt voor tests                                         |

## Basisgebruik

### Concurrency::run()

Als je een array van closures aan de `run()`-methode doorgeeft, worden deze parallel uitgevoerd.
Als resultaat ontvang je een array met de returnwaarden van elke closure.

```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(),
]);
```

Met array-destructuring kun je elk resultaat in een variabele opvangen.
De volgorde van de closures en de volgorde van de returnwaarden komen overeen.

### Een driver kiezen

Wil je een specifieke driver gebruiken, dan geef je die op met de `driver()`-methode.

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

Om de standaarddriver te wijzigen, publiceer je het configuratiebestand en pas je de `default`-optie aan.

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

## De fork-driver gebruiken

De `fork`-driver is sneller dan de `process`-driver, maar kan alleen worden gebruikt in de PHP CLI-omgeving (Artisan-commando's en queue workers).
Binnen webrequests kun je hem niet gebruiken.

Installeer vóór gebruik het `spatie/fork`-package.

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

<Warning>
  De `fork`-driver werkt niet tijdens webrequests. Gebruik hem als je parallelle verwerking wilt uitvoeren binnen Artisan-commando's of queue workers.
</Warning>

## Geen resultaat nodig: Concurrency::defer()

Als je niet geïnteresseerd bent in het resultaat en de taken op de achtergrond wilt uitvoeren nadat de HTTP-response is verstuurd, gebruik je de `defer()`-methode.

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

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

Op het moment dat je `defer()` aanroept, worden de closures nog niet uitgevoerd.
Ze worden parallel uitgevoerd nadat de HTTP-response naar de gebruiker is verzonden.

<Tip>
  `defer()` is ideaal voor taken waarop de gebruiker niet hoeft te wachten, zoals het vastleggen van analytics-data of het opwarmen van de cache.
</Tip>

## Praktijkvoorbeeld: meerdere externe API's tegelijk aanroepen

Neem als voorbeeld een dashboard van een webshop dat informatie ophaalt uit drie API's: voorraadbeheer, verkoopcijfers en verzendstatus.

### Sequentiële uitvoering (vóór verbetering)

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

public function dashboard(): array
{
    // Wachten tot elk request na elkaar is afgerond (totaal circa 3 seconden)
    $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');
}
```

### Parallelle uitvoering (na verbetering)

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

public function dashboard(): array
{
    // De drie requests tegelijk uitvoeren (klaar in de tijd van de traagste API)
    [$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');
}
```

Als elke API 1 seconde nodig heeft, duurt sequentiële uitvoering in totaal 3 seconden, terwijl parallelle uitvoering in ongeveer 1 seconde klaar is.

### Meerdere database-aggregaties tegelijk uitvoeren

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

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 [
        'total_users'  => $totalUsers,
        'active_users' => $activeUsers,
        'total_orders' => $totalOrders,
        'revenue'      => $revenue,
    ];
}
```

<Tip>
  Als je de `process`-driver gebruikt voor database-aggregaties, wordt er per childproces een nieuwe databaseverbinding opgezet.
  Let bij veel gelijktijdige uitvoeringen op het maximale aantal databaseverbindingen.
</Tip>

## Configuratie voor tests

In de testomgeving voert de `sync`-driver de closures sequentieel uit.
Er zijn geen opstartkosten voor processen, waardoor je tests sneller worden.

```php theme={null}
// tests/Feature/DashboardTest.php
use Illuminate\Support\Facades\Concurrency;

public function test_dashboard_returns_correct_data(): void
{
    Concurrency::fake(); // Overschakelen naar de sync-driver

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

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

Je kunt de standaarddriver ook wijzigen in `.env.testing`.

```ini theme={null}
CONCURRENCY_DRIVER=sync
```

## Aandachtspunten

<AccordionGroup>
  <Accordion title="Beperkingen van closures">
    Closures worden geserialiseerd en doorgegeven aan het childproces.
    Objecten die niet geserialiseerd kunnen worden (databaseverbindingen, file handles, resources enzovoort) kun je niet van buiten de closure capturen.
    Maak benodigde objecten binnen de closure opnieuw aan.

    ```php theme={null}
    // NG: een Eloquent-model van buitenaf capturen
    $user = User::find(1);
    Concurrency::run([
        fn () => $user->orders()->count(), // Kan mogelijk niet geserialiseerd worden
    ]);

    // OK: de data binnen de closure ophalen
    $userId = 1;
    Concurrency::run([
        fn () => Order::where('user_id', $userId)->count(),
    ]);
    ```
  </Accordion>

  <Accordion title="Overhead van de process-driver">
    De `process`-driver heeft opstartkosten voor childprocessen. Als de taak zelf heel kort is (enkele milliseconden of minder), levert parallelle uitvoering mogelijk geen snelheidswinst op.
    Parallellisatie loont vooral bij taken die 100 ms of langer duren, zoals HTTP-requests of database-aggregaties.
  </Accordion>

  <Accordion title="Beperkingen van de fork-driver">
    De `fork`-driver forkt PHP-processen en kan daarom niet worden gebruikt tijdens webrequests (FPM of Apache).
    Gebruik hem alleen binnen Artisan-commando's of queue workers.
  </Accordion>

  <Accordion title="Omgaan met exceptions">
    Als er tijdens de parallelle uitvoering een exception optreedt, gooit `run()` die exception opnieuw.
    Wil je dat de andere taken doorgaan als één taak een exception veroorzaakt, gebruik dan `try/catch` binnen de closure.

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

## Volgende stap

<Card title="Queues en jobs" icon="layer-group" href="/nl/queues">
  Leer hoe je met queues en jobs taken asynchroon op de achtergrond uitvoert
</Card>


## Related topics

- [Concurrency](/nl/packages/laravel-copilot-sdk/concurrency.md)
- [Upgradegids van Laravel 11 naar 12](/nl/blog/upgrade-11-to-12.md)
- [Laravel-updates van april 2026](/nl/blog/changelog/202604.md)
- [Ontwikkelgids voor apps met de engine-API - VOICEVOX for Laravel](/nl/packages/laravel-voicevox/app-guide.md)
