Skip to main content

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

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.

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.
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.
Om de standaarddriver te wijzigen, publiceer je het configuratiebestand en pas je de default-optie aan.

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.
De fork-driver werkt niet tijdens webrequests. Gebruik hem als je parallelle verwerking wilt uitvoeren binnen Artisan-commando’s of queue workers.

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.
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.
defer() is ideaal voor taken waarop de gebruiker niet hoeft te wachten, zoals het vastleggen van analytics-data of het opwarmen van de cache.

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)

Parallelle uitvoering (na verbetering)

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

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.

Automatische propagatie van Context

In Laravel 13.31 (#61419) is de process-driver zo aangepast dat hij de huidige Context automatisch doorgeeft aan childprocessen. Voorheen werden waarden die met Context::add() waren ingesteld niet overgenomen door childprocessen die met Concurrency::run() of Concurrency::defer() werden gestart, waardoor de context in de logs van het parent- en het childproces uit elkaar kon lopen. Na de fix wordt intern hetzelfde mechanisme gebruikt als voor het doorgeven van context aan queue-jobs.
  1. Vlak voordat het childproces wordt gestart, wordt het resultaat van Context::dehydrate() naar JSON geserialiseerd en als de omgevingsvariabele __LARAVEL_CONTEXT doorgegeven
  2. Zodra het childproces (het verborgen Artisan-commando invoke-serialized-closure) start, wordt __LARAVEL_CONTEXT uitgelezen en met hydrate() in de Context-repository geladen
Daardoor kun je binnen de closure zonder problemen de context gebruiken die je erbuiten hebt ingesteld.
De fork-driver gebruikt de OS-fork() van spatie/fork, waardoor het childproces met een exacte kopie van de geheugenruimte van het parentproces start. De context wordt daardoor van nature al gedeeld, en deze fix heeft dus vooral betrekking op de process-driver (die een apart proces opstart).
Door waarden als request-ID’s en user-ID’s — die je gebruikt voor logcorrelatie — in Context te zetten, kun je ook de logs van parallel uitgevoerde processen met Concurrency::run() op dezelfde sleutels terugvinden. Zie de context delen voor meer details.

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.
Je kunt de standaarddriver ook wijzigen in .env.testing.

Aandachtspunten

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

Volgende stap

Queues en jobs

Leer hoe je met queues en jobs taken asynchroon op de achtergrond uitvoert
Laatst gewijzigd op 11 september 2026