

Le traduzioni sono generate tramite traduzione automatica. In caso di conflitto tra il contenuto di una traduzione e la versione originale in Inglese, quest'ultima prevarrà.

# Ripetere le attività non andate a buon fine
<a name="features-retry"></a>

A volte le attività non vanno a buon fine per ragioni effimere, ad esempio una perdita temporanea della connessione. In altri casi l'attività va a buon fine, quindi il modo corretto di gestire l'errore è spesso quello di ripetere l'attività, anche più volte. 

Esiste una serie di strategie per ripetere le attività; la migliore dipende dai dettagli del flusso di lavoro. Tali strategie rientrano in tre categorie di base: 
+ La retry-until-success strategia continua semplicemente a riprovare l'attività fino al suo completamento.
+ La strategia di ripetizione esponenziale aumenta esponenzialmente l'intervallo di tempo tra i tentativi fino al completamento dell'attività o fino a quando il processo raggiunge un punto di arresto specifico, come un numero massimo di tentativi.
+ La strategia di ripetizione personalizzata decide se o come ripetere l'attività dopo ciascun tentativo non andato a buon fine.

Le sezioni seguenti descrivono come implementare queste strategie. I lavoratori del flusso di lavoro di esempio utilizzano tutti una singola attività, `unreliableActivity`, che esegue casualmente una delle seguenti operazioni: 
+ Viene completata immediatamente
+ Non va a buon fine intenzionalmente superando il valore di timeout
+ Non va a buon fine intenzionalmente generando `IllegalStateException` 

## Retry-Until-Success Strategia
<a name="features-retry-success"></a>

La strategia più semplice è quella di ripetere l'attività ogni volta che ha esito negativo fino al buon esito. Il modello di base è:

1. Implementare una classe nidificata `TryCatch` o `TryCatchFinally` nel metodo del punto di ingresso del flusso di lavoro.

1. Eseguire l'attività in `doTry`.

1. Se l'attività non va a buon fine, il framework chiama `doCatch`, che esegue nuovamente il metodo del punto di ingresso.

1. Ripetere le fasi 2 e 3 fino al completamento con esito positivo dell'attività.

Il seguente flusso di lavoro implementa la retry-until-success strategia. L'interfaccia del flusso di lavoro è implementata in `RetryActivityRecipeWorkflow` e ha un metodo, `runUnreliableActivityTillSuccess`, che è il punto di ingresso del flusso di lavoro. Il lavoratore del flusso di lavoro viene implementato in `RetryActivityRecipeWorkflowImpl`, nel seguente modo: 

```
public class RetryActivityRecipeWorkflowImpl
    implements RetryActivityRecipeWorkflow {

    @Override
    public void runUnreliableActivityTillSuccess() {
        final Settable<Boolean> retryActivity = new Settable<Boolean>();

        new TryCatch() {
            @Override
            protected void doTry() throws Throwable {
                Promise<Void> activityRanSuccessfully
                    = client.unreliableActivity();
                setRetryActivityToFalse(activityRanSuccessfully, retryActivity);
            }

            @Override
            protected void doCatch(Throwable e) throws Throwable {
                retryActivity.set(true);
            }
        };
        restartRunUnreliableActivityTillSuccess(retryActivity);
    }

    @Asynchronous
    private void setRetryActivityToFalse(
            Promise<Void> activityRanSuccessfully,
            @NoWait Settable<Boolean> retryActivity) {
        retryActivity.set(false);
    }

    @Asynchronous
    private void restartRunUnreliableActivityTillSuccess(
            Settable<Boolean> retryActivity) {
        if (retryActivity.get()) {
            runUnreliableActivityTillSuccess();
        }
    }
}
```

Il flusso di lavoro funziona come segue: 

1. `runUnreliableActivityTillSuccess` crea un oggetto `Settable<Boolean>` denominato `retryActivity` che viene usato per indicare se l'attività non è riuscita e deve essere ritentata. `Settable<T>` è derivato da `Promise<T>` e funziona allo stesso modo ma il valore dell'oggetto `Settable<T>` viene impostato manualmente.

1. `runUnreliableActivityTillSuccess` implementa una classe annidata anonima `TryCatch` per gestire le eccezioni generate dall'attività `unreliableActivity`. Per ulteriori discussioni su come gestire le eccezioni generate da un codice asincrono, consulta [Gestione errori](errorhandling.md).

1. `doTry` esegue l'attività `unreliableActivity`, che restituisce un oggetto `Promise<Void>` di nome `activityRanSuccessfully`.

1. `doTry` chiama il metodo asincrono `setRetryActivityToFalse`, che ha due parametri:
   + `activityRanSuccessfully` accetta l'oggetto `Promise<Void>` restituito dall'attività `unreliableActivity`.
   + `retryActivity` accetta l'oggetto `retryActivity`.

   Se `unreliableActivity` viene completato, `activityRanSuccessfully` diventa pronto e `setRetryActivityToFalse` imposta `retryActivity` su false. In caso contrario, `activityRanSuccessfully` non diventa mai pronto e `setRetryActivityToFalse` non viene eseguito.

1. Se `unreliableActivity` genera un'eccezione, il framework chiama `doCatch` e lo trasferisce all'oggetto dell'eccezione. `doCatch` imposta `retryActivity` su true.

1. `runUnreliableActivityTillSuccess` chiama il metodo asincrono `restartRunUnreliableActivityTillSuccess` e lo trasferisce all'oggetto `retryActivity`. Poiché `retryActivity` è un tipo `Promise<T>`, `restartRunUnreliableActivityTillSuccess` ritarda l'esecuzione fin quando `retryActivity` è pronto, il che si verifica dopo il completamento di `TryCatch`. 

1. Quando `retryActivity` è pronto, `restartRunUnreliableActivityTillSuccess` estrae il valore.
   + Se il valore è `false`, il nuovo tentativo è andato a buon fine. `restartRunUnreliableActivityTillSuccess` non è operativo e la sequenza di ripetizione termina.
   + Se il valore è true, il nuovo tentativo non è andato a buon fine. `restartRunUnreliableActivityTillSuccess` chiama `runUnreliableActivityTillSuccess` per eseguire nuovamente l'attività.

1. Si ripetono le fasi 1-7 fino al completamento di `unreliableActivity`. 

**Nota**  
`doCatch` non gestisce l'eccezione; imposta semplicemente l'oggetto `retryActivity` su true per indicare l'esito negativo dell'attività. La ripetizione è gestita dal metodo asincrono `restartRunUnreliableActivityTillSuccess`, che ritarda l'esecuzione fino al completamento di `TryCatch`. Il motivo di questo approccio è che se riprovi un'attività in `doCatch` non puoi annullarla. Ripetere l'attività in `restartRunUnreliableActivityTillSuccess` ti permette di eseguire attività annullabili. 

## Strategia di ripetizione esponenziale
<a name="features-retry-exponential"></a>

Con la strategia di ripetizione esponenziale, il framework esegue nuovamente un'attività non andata a buon fine dopo un periodo di tempo specifico, N secondi. Se il tentativo ha esito negativo, il framework esegue nuovamente l'attività dopo 2N secondi, 4N secondi e così via. Poiché il tempo di attesa può essere lungo, in genere i tentativi si arrestano a un certo punto invece che proseguire all'infinito.

Il framework prevede tre modi per implementare una strategia di ripetizione esponenziale:
+ L'annotazione `@ExponentialRetry` è l'approccio più semplice, ma devi impostare le opzioni di configurazione della ripetizione al momento della compilazione.
+ La classe `RetryDecorator` ti permette di impostare la configurazione della ripetizione in fase di runtime e di modificarla in base alle necessità.
+ La classe `AsyncRetryingExecutor` ti permette di impostare la configurazione della ripetizione in fase di runtime e di modificarla in base alle necessità. Inoltre, il framework chiama un metodo `AsyncRunnable.run` implementato dall'utente per eseguire ogni tentativo di ripetizione.

Tutti gli approcci supportano le seguenti opzioni di configurazione, in cui i valori di tempo sono espressi in secondi: 
+ Il tempo di attesa per la ripetizione iniziale.
+ Il coefficiente di backoff, che viene utilizzato per calcolare gli intervalli di ripetizione, nel modo seguente:

  ```
  retryInterval = initialRetryIntervalSeconds * Math.pow(backoffCoefficient, numberOfTries - 2)
  ```

  Il valore predefinito è 2.0.
+ Il numero massimo di tentativi di ripetizione. Il valore predefinito è illimitato.
+ L'intervallo massimo di ripetizione. Il valore predefinito è illimitato.
+ Il tempo di scadenza. I tentativi si arrestano quando la durata totale del processo supera questo valore. Il valore predefinito è illimitato.
+ Le eccezioni che attivano il processo di ripetizione. Per impostazione predefinita, tutte le eccezioni attivano il processo di ripetizione.
+ Le eccezioni che non attivano tentativi di ripetizione. Per impostazione predefinita, non è esclusa alcuna eccezione.

Le sezioni seguenti descrivono i vari modi in cui è possibile implementare una strategia di ripetizione esponenziale.

### Riprova esponenziale con @ ExponentialRetry
<a name="features-retry-exponential-annotation"></a>

Il modo più semplice per implementare una strategia di ripetizione esponenziale per un'attività è applicare un'annotazione `@ExponentialRetry ` all'attività nella definizione dell'interfaccia. Se l'attività non va a buon fine, il framework gestisce automaticamente il processo di ripetizione in base ai valori opzionali specificati. Il modello di base è:

1. Applica `@ExponentialRetry` alle attività in questione e specifica la configurazione di ripetizione.

1. Se l'attività annotata non va a buon fine, il framework la recupera automaticamente secondo la configurazione specificata dagli argomenti dell'annotazione. 

Il lavoratore del flusso di lavoro `ExponentialRetryAnnotationWorkflow` implementa la strategia di ripetizione esponenziale utilizzando un'annotazione `@ExponentialRetry`. Utilizza un'attività `unreliableActivity` la cui definizione dell'interfaccia è implementata in `ExponentialRetryAnnotationActivities`, nel modo seguente:

```
@Activities(version = "1.0")
@ActivityRegistrationOptions(
    defaultTaskScheduleToStartTimeoutSeconds = 30,
    defaultTaskStartToCloseTimeoutSeconds = 30)
public interface ExponentialRetryAnnotationActivities {
    @ExponentialRetry(
        initialRetryIntervalSeconds = 5,
        maximumAttempts = 5,
        exceptionsToRetry = IllegalStateException.class)
    public void unreliableActivity();
}
```

Le opzioni di `@ExponentialRetry` specificano la seguente strategia: 
+ Ripeti sono se l'attività genera `IllegalStateException`.
+ Utilizza un tempo di attesa iniziale di 5 secondi.
+ Non più di 5 tentativi di ripetizione. 

L'interfaccia del flusso di lavoro è implementata in `RetryWorkflow` e ha un metodo, `process`, che è il punto di ingresso del flusso di lavoro. Il lavoratore del flusso di lavoro viene implementato in `ExponentialRetryAnnotationWorkflowImpl`, nel seguente modo: 

```
public class ExponentialRetryAnnotationWorkflowImpl implements RetryWorkflow {
    public void process() {
        handleUnreliableActivity();
    }

    public void handleUnreliableActivity() {
        client.unreliableActivity();
    }
}
```

Il flusso di lavoro funziona come segue: 

1. `process` esegue il metodo asincrono `handleUnreliableActivity`.

1. `handleUnreliableActivity` esegue l'attività `unreliableActivity`. 

Se l'attività non va a buon fine generando `IllegalStateException`, il framework esegue automaticamente la strategia di ripetizione specificata in `ExponentialRetryAnnotationActivities`.

### Riprova esponenziale con la classe RetryDecorator
<a name="features-retry-exponential-decorator"></a>

`@ExponentialRetry` è semplice da usare. Tuttavia, la configurazione è statica e impostata al momento della compilazione, in modo che il framework utilizzi la stessa strategia di ripetizione ogni volta che l'attività non va a buon fine. Puoi implementare una strategia di ripetizione esponenziale più flessibile utilizzando la classe `RetryDecorator`, che ti permette di specificare la configurazione in fase di runtime e di modificarla in base alle necessità. Il modello di base è:

1. Crea e configura un oggetto `ExponentialRetryPolicy` che specifichi la configurazione della ripetizione.

1. Crea un oggetto `RetryDecorator` e trasferisci l'oggetto `ExponentialRetryPolicy` della Fase 1 al costruttore.

1. Applica l'oggetto decorator all'attività trasferendo il nome della classe del client di attività al metodo decorato dell'oggetto `RetryDecorator`.

1. Esegui l'attività.

Se l'attività non va a buon fine, il framework la ripete secondo la configurazione dell'oggetto `ExponentialRetryPolicy`. Puoi modificare la configurazione della ripetizione in base alla necessità cambiando l'oggetto. 

**Nota**  
L'annotazione `@ExponentialRetry` e la classe `RetryDecorator` sono reciprocamente esclusive. Non puoi utilizzare `RetryDecorator` per sovrascrivere dinamicamente una policy di ripetizione specificata da un'annotazione `@ExponentialRetry`. 

La seguente implementazione del flusso di lavoro mostra come utilizzare la classe `RetryDecorator` per implementare una strategia di ripetizione esponenziale. Utilizza un'attività `unreliableActivity` priva dell'annotazione `@ExponentialRetry`. L'interfaccia del flusso di lavoro è implementata in `RetryWorkflow` e ha un metodo, `process`, che è il punto di ingresso del flusso di lavoro. Il lavoratore del flusso di lavoro viene implementato in `DecoratorRetryWorkflowImpl`, nel seguente modo: 

```
public class DecoratorRetryWorkflowImpl implements RetryWorkflow {
   ...
  public void process() {
      long initialRetryIntervalSeconds = 5;
      int maximumAttempts = 5;
      ExponentialRetryPolicy retryPolicy = new ExponentialRetryPolicy(
              initialRetryIntervalSeconds).withMaximumAttempts(maximumAttempts);

      Decorator retryDecorator = new RetryDecorator(retryPolicy);
      client = retryDecorator.decorate(RetryActivitiesClient.class, client);
      handleUnreliableActivity();
  }

  public void handleUnreliableActivity() {
      client.unreliableActivity();
  }
}
```

Il flusso di lavoro funziona come segue: 

1. `process` crea e configura un oggetto `ExponentialRetryPolicy` nel seguente modo: 
   + Trasferendo al costruttore l'intervallo di ripetizione iniziale.
   + Chiamando il metodo `withMaximumAttempts` dell'oggetto per impostare il numero massimo di tentativi a 5. `ExponentialRetryPolicy` espone altri oggetti `with` che è possibile usare per specificare altre opzioni di configurazione.

1. `process` crea un oggetto `RetryDecorator` con nome `retryDecorator` e trasferisce l'oggetto `ExponentialRetryPolicy` della Fase 1 al costruttore.

1. `process` applica l'elemento decorator all'attività chiamando il metodo `retryDecorator.decorate` e trasferendolo al nome della classe del client di attività.

1. `handleUnreliableActivity` esegue l'attività. 

Se l'attività non va a buon fine, il framework la ripete secondo la configurazione specificata nella Fase 1.

**Nota**  
Molti dei metodi `with` della classe `ExponentialRetryPolicy` hanno un metodo corrispondente `set` che puoi chiamare per modificare l'opzione di configurazione corrispondente in qualsiasi momento: `setBackoffCoefficient`, `setMaximumAttempts`, `setMaximumRetryIntervalSeconds` e `setMaximumRetryExpirationIntervalSeconds`. 

### Riprova esponenziale con la classe AsyncRetryingExecutor
<a name="features-retry-exponential-async"></a>

La classe `RetryDecorator` offre più flessibilità nella configurazione del processo di ripetizione rispetto a `@ExponentialRetry`, ma il framework esegue comunque automaticamente i tentativi di ripetizione, in base alla attuale configurazione dell'oggetto `ExponentialRetryPolicy`. Un approccio più flessibile prevede l'utilizzo della classe `AsyncRetryingExecutor`. Oltre a permetterti di configurare il processo di ripetizione in fase di runtime, il framework chiama un metodo `AsyncRunnable.run` implementato dall'utente per eseguire ogni tentativo di ripetizione invece che eseguire semplicemente l'attività.

Il modello di base è: 

1. Crea e configura un oggetto `ExponentialRetryPolicy` per specificare la configurazione della ripetizione.

1. Crea un oggetto `AsyncRetryingExecutor` e trasferiscigli l'oggetto `ExponentialRetryPolicy` e un'istanza dell'orologio del flusso di lavoro.

1.  Implementa una classe annidata anonima `TryCatch` o `TryCatchFinally`.

1. Implementa una classe anonima `AsyncRunnable` e sovrascrivi il metodo `run` per implementare il codice personalizzato per eseguire l'attività.

1.  Sovrascrivi `doTry` per chiamare il metodo `execute` dell'oggetto `AsyncRetryingExecutor` e trasferirlo alla classe `AsyncRunnable` dalla fase 4. L'oggetto `AsyncRetryingExecutor` chiama `AsyncRunnable.run` per eseguire l'attività.

1. Se l'attività non va a buon fine, l'oggetto `AsyncRetryingExecutor` chiama nuovamente il metodo `AsyncRunnable.run` secondo la policy di ripetizione specificata nella Fase 1. 

Il flusso di lavoro seguente mostra come utilizzare la classe `AsyncRetryingExecutor` per implementare una strategia di ripetizione esponenziale. Utilizza la stessa attività `unreliableActivity` del flusso di lavoro `DecoratorRetryWorkflow` discusso in precedenza. L'interfaccia del flusso di lavoro è implementata in `RetryWorkflow` e ha un metodo, `process`, che è il punto di ingresso del flusso di lavoro. Il lavoratore del flusso di lavoro viene implementato in `AsyncExecutorRetryWorkflowImpl`, nel seguente modo: 

```
public class AsyncExecutorRetryWorkflowImpl implements RetryWorkflow {
  private final RetryActivitiesClient client = new RetryActivitiesClientImpl();
  private final DecisionContextProvider contextProvider = new DecisionContextProviderImpl();
  private final WorkflowClock clock = contextProvider.getDecisionContext().getWorkflowClock();

  public void process() {
      long initialRetryIntervalSeconds = 5;
      int maximumAttempts = 5;
      handleUnreliableActivity(initialRetryIntervalSeconds, maximumAttempts);
  }
  public void handleUnreliableActivity(long initialRetryIntervalSeconds, int maximumAttempts) {

      ExponentialRetryPolicy retryPolicy = new ExponentialRetryPolicy(initialRetryIntervalSeconds).withMaximumAttempts(maximumAttempts);
      final AsyncExecutor executor = new AsyncRetryingExecutor(retryPolicy, clock);

      new TryCatch() {
          @Override
          protected void doTry() throws Throwable {
              executor.execute(new AsyncRunnable() {
                  @Override
                  public void run() throws Throwable {
                      client.unreliableActivity();
                  }
              });
          }
          @Override
          protected void doCatch(Throwable e) throws Throwable {
          }
      };
  }
}
```

Il flusso di lavoro funziona come segue: 

1. `process` chiama il metodo `handleUnreliableActivity` e lo trasferisce alle impostazioni della configurazione.

1. `handleUnreliableActivity` utilizza le impostazioni di configurazione della Fase 1 per creare un oggetto `ExponentialRetryPolicy`, l'oggetto `retryPolicy`.

1. `handleUnreliableActivity` crea un oggetto `AsyncRetryExecutor`, `executor` e trasferisce l'oggetto `ExponentialRetryPolicy` della Fase 2 e un'istanza dell'orologio del flusso di lavoro al costruttore

1.  `handleUnreliableActivity` implementa una classe annidata anonima `TryCatch` e sovrascrive i metodi `doTry` e `doCatch` per eseguire i tentativi di ripetizione e gestire le eventuali eccezioni.

1. `doTry` crea una classe anonima `AsyncRunnable` e sovrascrive il metodo `run` per implementare il codice personalizzato per eseguire `unreliableActivity`. Per semplicità, `run` esegue semplicemente l'attività, ma puoi implementare approcci più sofisticati in base alle necessità.

1. `doTry` chiama `executor.execute` e lo trasferisce all'oggetto `AsyncRunnable`. `execute` chiama il metodo `AsyncRunnable` dell'oggetto `run` per eseguire l'attività.

1. Se l'attività non va a buon fine, l'esecutore chiama di nuovo `run` in base alla configurazione dell'oggetto `retryPolicy`. 

Per ulteriori discussioni su come utilizzare la classe `TryCatch` per gestire gli errori, consulta [AWS Flow Framework per le eccezioni Java](errorhandling.exceptions.md). 

## Strategia di ripetizione personalizzata
<a name="custom-retry-strategy"></a>

L'approccio più flessibile per riprovare le attività non riuscite è una strategia personalizzata, che richiama ricorsivamente un metodo asincrono che esegue il tentativo di nuovo tentativo, proprio come la strategia. retry-until-success Tuttavia, invece che rieseguire semplicemente l'attività, implementi una logica personalizzata che decide se e come eseguire i successivi tentativi di ripetizione. Il modello di base è: 

1. Crea un oggetto di stato `Settable<T>`, che viene utilizzato per indicare se l'attività non è andata a buon fine.

1. Implementa una classe annidata `TryCatch` o `TryCatchFinally`.

1. `doTry` esegue l'attività.

1. Se l'attività non va a buon fine, `doCatch` imposta l'oggetto di stato per indicare che l'attività ha avuto esito negativo.

1. Chiama un metodo asincrono di gestione dell'errore e trasferiscilo all'oggetto di stato. Il metodo ritarda l'esecuzione fino al completamento di `TryCatch` o `TryCatchFinally`.

1. Il metodo di gestione dell'errore decide se e quando ripetere l'attività.

Il flusso di lavoro seguente mostra come implementare una strategia di ripetizione personalizzata. Utilizza la stessa attività `unreliableActivity` dei flussi di lavoro `DecoratorRetryWorkflow` e `AsyncExecutorRetryWorkflow`. L'interfaccia del flusso di lavoro è implementata in `RetryWorkflow` e ha un metodo, `process`, che è il punto di ingresso del flusso di lavoro. Il lavoratore del flusso di lavoro viene implementato in `CustomLogicRetryWorkflowImpl`, nel seguente modo: 

```
public class CustomLogicRetryWorkflowImpl implements RetryWorkflow {
  ...
  public void process() {
      callActivityWithRetry();
  }
  @Asynchronous
  public void callActivityWithRetry() {
      final Settable<Throwable> failure = new Settable<Throwable>();
      new TryCatchFinally() {
          protected void doTry() throws Throwable {
              client.unreliableActivity();
          }
          protected void doCatch(Throwable e) {
              failure.set(e);
          }
          protected void doFinally() throws Throwable {
              if (!failure.isReady()) {
                  failure.set(null);
              }
          }
      };
      retryOnFailure(failure);
  }
  @Asynchronous
  private void retryOnFailure(Promise<Throwable> failureP) {
      Throwable failure = failureP.get();
      if (failure != null && shouldRetry(failure)) {
          callActivityWithRetry();
      }
  }
  protected Boolean shouldRetry(Throwable e) {
      //custom logic to decide to retry the activity or not
      return true;
  }
}
```

Il flusso di lavoro funziona come segue:

1. `process` chiama il metodo asincrono `callActivityWithRetry`.

1. `callActivityWithRetry` crea un errore di oggetto `Settable<Throwable>` denominato che viene utilizzato per indicare se l'attività non è andata a buon fine. `Settable<T>` deriva da `Promise<T>` e funziona quasi allo stesso modo, ma il valore dell'oggetto `Settable<T>` è impostato manualmente.

1. `callActivityWithRetry` implementa una classe annidata anonima `TryCatchFinally` per gestire le eccezioni generate da `unreliableActivity`. Per ulteriori discussioni su come gestire le eccezioni generate da un codice asincrono, consulta [AWS Flow Framework per le eccezioni Java](errorhandling.exceptions.md).

1. `doTry` esegue `unreliableActivity`.

1. Se `unreliableActivity` genera un'eccezione, il framework chiama `doCatch` e lo trasferisce all'oggetto dell'eccezione. `doCatch` imposta `failure` sull'oggetto dell'eccezione, il che indica che l'attività non è andata a buon fine e mette l'oggetto in stato di pronto.

1. `doFinally` verifica se `failure` è pronto, che sarà vero solo se `failure` è stato impostato da `doCatch`.
   + Se è pronto, non fa nulla. `failure` `doFinally`
   + Se `failure` non è pronto, l'attività viene completata e `doFinally` imposta l'errore su `null`. 

1. `callActivityWithRetry` chiama il metodo asincrono `retryOnFailure` e vi trasferisce l'errore. Poiché l'errore è un tipo `Settable<T>`, `callActivityWithRetry` ritarda l'esecuzione fin quando l'errore è pronto, il che si verifica dopo il completamento di `TryCatchFinally`.

1. `retryOnFailure` riceve il valore dall'errore.
   + Se l'errore è impostato su null, il tentativo di ripetizione è andato a buon fine. `retryOnFailure` non fa alcunché, il che termina il processo di ripetizione.
   + Se l'errore è impostato su un oggetto di eccezione e `shouldRetry` restituisce il valore true, `retryOnFailure` chiama `callActivityWithRetry` per riprovare l'attività. 

     `shouldRetry` implementa una logica personalizzata per decidere se ripetere un'attività dall'esito negativo. Per semplicità, `shouldRetry` restituisce sempre il valore `true` e `retryOnFailure` esegue immediatamente l'attività, ma puoi implementare una logica più sofisticata in base alle necessità. 

1. I passaggi da 2 a 8 si ripetono fino al `unreliableActivity` completamento o alla `shouldRetry` decisione di interrompere il processo. 

**Nota**  
`doCatch` non gestisce il processo di ripetizione; imposta semplicemente l'errore per indicare l'esito negativo dell'attività. Il processo di ripetizione è gestito dal metodo asincrono `retryOnFailure`, che ritarda l'esecuzione fino al completamento di `TryCatch`. Il motivo di questo approccio è che se riprovi un'attività in `doCatch` non puoi annullarla. Ripetere l'attività in `retryOnFailure` ti permette di eseguire attività annullabili. 