
# Passaggi paralleli

A volte più passaggi possono essere eseguiti contemporaneamente. Il metodo `->parallel()` avvia un gruppo di gestori in parallelo. Il flusso di lavoro avanza solo quando tutti i gestori paralleli sono terminati.

## Il metodo `->parallel()`

```php
use Queuety\Queuety;

Queuety::workflow( 'enrich_profile' )
    ->then( FetchUserHandler::class )
    ->parallel( [
        FetchSocialProfileHandler::class,
        FetchPaymentHistoryHandler::class,
        FetchActivityLogHandler::class,
    ] )
    ->then( MergeAndSaveHandler::class )
    ->dispatch( [ 'user_id' => 42 ] );
```

In questo esempio:
1. `FetchUserHandler` viene eseguito per primo (in sequenza)
2. Tre gestori vengono eseguiti contemporaneamente (in parallelo)
3. `MergeAndSaveHandler` viene eseguito dopo il completamento di tutti e tre (in sequenza)

## Come funziona

Quando si raggiunge un passaggio parallelo, Queuety avvia un job per ogni gestore del gruppo. Ogni job riceve lo stato corrente del flusso di lavoro e viene eseguito in modo indipendente. Il flusso di lavoro tiene traccia del numero di job paralleli ancora in corso e avanza al passaggio successivo solo quando sono tutti terminati.

Il valore restituito da ogni gestore parallelo viene unito allo stato del flusso di lavoro. Se più gestori restituiscono la stessa chiave, prevale il valore del gestore che termina per ultimo.

## Unione dello stato

Ogni gestore parallelo restituisce dati che vengono uniti allo stato condiviso del flusso di lavoro:

```php
class FetchSocialProfileHandler implements Step {
    public function handle( array $state ): array {
        $profile = fetch_social_api( $state['user_id'] );
        return [ 'social_profile' => $profile ];
    }

    public function config(): array {
        return [];
    }
}

class FetchPaymentHistoryHandler implements Step {
    public function handle( array $state ): array {
        $payments = fetch_payment_api( $state['user_id'] );
        return [ 'payment_history' => $payments ];
    }

    public function config(): array {
        return [];
    }
}
```

Al termine del passaggio parallelo, lo stato contiene sia `social_profile` sia `payment_history`, e il passaggio sequenziale successivo può accedere a entrambi.

## Combinare sequenza e parallelismo

Potete combinare liberamente le chiamate `->then()` e `->parallel()`:

```php
Queuety::workflow( 'complex_pipeline' )
    ->then( SetupHandler::class )               // sequential
    ->parallel( [ TaskA::class, TaskB::class ] ) // parallel
    ->then( IntermediateHandler::class )         // sequential
    ->parallel( [ TaskC::class, TaskD::class ] ) // parallel
    ->then( FinalizeHandler::class )             // sequential
    ->dispatch( $payload );
```

## Gestione degli errori

Se un gestore di un gruppo parallelo non riesce, il flusso di lavoro segue la normale logica dei tentativi per quel gestore. Gli altri gestori paralleli già completati non vengono eseguiti di nuovo. Quando il gestore in errore riesce, il flusso di lavoro prosegue. Se esaurisce i tentativi e viene sepolto, il flusso di lavoro viene contrassegnato come non riuscito e i job dei rami rimanenti vengono sepolti; usate `retry_workflow()` per eseguire di nuovo il passaggio non riuscito.
