Guide de JDeferred

Guide de JDeferred

1. Vue d'ensemble

JDeferred est une petite bibliothèqueJava (prend également en chargeGroovy) utilisée pour implémenter une topologie asynchrone sans écrire de code standard. Ce framework est inspiré de la fonctionnalitéJquery’s Promise/Ajax et du modèleAndroid’s Deferred Object.

Dans ce didacticiel, nous allons montrer comment utiliserJDeferred et ses différents utilitaires.

2. Dépendance Maven

Nous pouvons commencer à utiliserJDeferred dans n'importe quelle application en ajoutant la dépendance suivante dans nospom.xml:


    org.jdeferred
    jdeferred-core
    1.2.6

Nous pouvons vérifier la dernière version du projetJDeferred dans lesCentral Maven Repository.

3. Promesses

Jetons un coup d'œil à un cas d'utilisation simple de l'appel d'un appel synchroneRESTAPI sujet aux erreurs et exécutons une tâche basée sur les données renvoyées par l'API.

Dans JQuery simple, le scénario ci-dessus peut être traité de la manière suivante:

$.ajax("/GetEmployees")
    .done(
        function() {
            alert( "success" );
        }
     )
    .fail(
        function() {
            alert( "error" );
        }
     )
    .always(
        function() {
            alert( "complete" );
        }
    );

De même,JDeferred est livré avec les interfacesPromise etDeferred qui enregistrent un hook indépendant du thread sur l'objet correspondant qui déclenche différentes actions personnalisables en fonction de l'état de cet objet.

Ici,Deferred agit comme déclencheur etPromise agit comme observateur.

Nous pouvons facilement créer ce type de flux de travail asynchrone:

Deferred deferred
  = new DeferredObject<>();
Promise promise = deferred.promise();

promise.done(result -> System.out.println("Job done"))
  .fail(rejection -> System.out.println("Job fail"))
  .progress(progress -> System.out.println("Job is in progress"))
  .always((state, result, rejection) ->
    System.out.println("Job execution started"));

deferred.resolve("msg");
deferred.notify("notice");
deferred.reject("oops");

Ici, chaque méthode a une sémantique différente:

  • done() - se déclenche uniquement lorsque les actions en attente sur l'objet différé sont / sont terminées avec succès

  • fail() - se déclenche alors qu'une exception est levée lors de l'exécution d'actions en attente sur l'objet différé

  • progress() - se déclenche dès que les actions en attente sur l'objet différé commencent à s'exécuter

  • always() - se déclenche quel que soit l'état de l'objet différé

Par défaut, l'état d'un objet différé peut êtrePENDING/REJECTED/RESOLVED. Nous pouvons vérifier l'état en utilisant la méthodedeferred.state().

Point à noter ici est queonce a deferred object’s status is changed to RESOLVED, we can’t perform reject operation on that object.

De même, une fois que l’état de l’objet est modifié enREJECTED,, nous ne pouvons pas effectuer d’opérationresolve ounotify sur cet objet. Toute violation entraînera unIllegalStateExeption.

4. Les filtres

Avant de récupérer le résultat final, nous pouvons effectuer un filtrage sur l'objet différé avecDoneFilter.

Une fois le filtrage terminé, nous obtenons l'objet différé thread-safe:

private static String modifiedMsg;

static String filter(String msg) {
    Deferred d = new DeferredObject<>();
    Promise p = d.promise();
    Promise filtered = p.then((result) > {
        modifiedMsg = "Hello "  result;
    });

    filtered.done(r > System.out.println("filtering done"));

    d.resolve(msg);
    return modifiedMsg;
}

5. Pipes

Semblable au filtre,JDeferred offre l'interfaceDonePipe pour effectuer des actions de post-filtrage sophistiquées une fois que les actions en attente d'objet différé sont résolues.

public enum Result {
    SUCCESS, FAILURE
};

private static Result status;

public static Result validate(int num) {
    Deferred d = new DeferredObject<>();
    Promise p = d.promise();

    p.then((DonePipe) result > {
        public Deferred pipeDone(Integer result) {
            if (result < 90) {
                return new DeferredObject()
                  .resolve(result);
            } else {
                return new DeferredObject()
                  .reject(new Exception("Unacceptable value"));
            }
    }).done(r > status = Result.SUCCESS )
      .fail(r > status = Result.FAILURE );

    d.resolve(num);
    return status;
}

Ici, en fonction de la valeur du résultat réel, nous avons soulevé une exception pour rejeter le résultat.

6. Gestionnaire différé

Dans un scénario en temps réel, nous devons traiter les multiples objets différés observés par plusieurs promesses. Dans ce scénario, il est assez difficile de gérer plusieurs promesses séparément.

C’est pourquoiJDeferred est livré avec l’interfaceDeferredManager qui crée un observateur commun pour toutes les promesses. Par conséquent, en utilisant cet observateur commun, nous pouvons créer des actions communes pour toutes les promesses:

Deferred deferred = new DeferredObject<>();
DeferredManager dm = new DefaultDeferredManager();
Promise p1 = deferred.promise(),
  p2 = deferred.promise(),
  p3 = deferred.promise();
dm.when(p1, p2, p3)
  .done(result -> ... )
  .fail(result -> ... );
deferred.resolve("Hello example");

Nous pouvons également assignerExecutorService avec un pool de threads personnalisé auxDeferredManager:

ExecutorService executor = Executors.newFixedThreadPool(10);
DeferredManager dm = new DefaultDeferredManager(executor);

En fait, nous pouvons complètement ignorer l'utilisation dePromise et définir directement l'interfaceCallable pour terminer la tâche:

DeferredManager dm = new DefaultDeferredManager();
dm.when(() -> {
    // return something and raise an exception to interrupt the task
}).done(result -> ... )
  .fail(e -> ... );

7. Action sans fil

Bien que, la plupart du temps, nous ayons à gérer un flux de travail asynchrone, nous devons parfois attendre les résultats de toutes les tâches parallèles.

Dans ce type de scénario, nous ne pouvons utiliser que la méthodewait() deObject pourwait for all deferred tasks to finish:

DeferredManager dm = new DefaultDeferredManager();
Deferred deferred = new DeferredObject<>();
Promise p1 = deferred.promise();
Promise p = dm
  .when(p1)
  .done(result -> ... )
  .fail(result -> ... );

synchronized (p) {
    while (p.isPending()) {
        try {
            p.wait();
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
}

deferred.resolve("Hello example");

Alternativement, nous pouvons utiliser la méthodewaitSafely() de l'interfacePromise pour obtenir la même chose.

try {
    p.waitSafely();
} catch (InterruptedException e) {
    e.printStackTrace();
}

Bien que les deux méthodes ci-dessus fonctionnent à peu près de la même manière, il est toujours conseillé d’utiliser la deuxième, car la deuxième procédure ne nécessite pas de synchronisation.

8. Intégration Android

JDeferred peut être facilement intégré aux applications Android en utilisant le plugin Android Maven.

Pour la construction APKLIB, nous devons ajouter la dépendance suivante dans lespom.xml:


    org.jdeferred
    jdeferred-android
    1.2.6
    apklib

Pour la construction deAAR, nous devons ajouter la dépendance suivante dans lepom.xml:


    org.jdeferred
    jdeferred-android-aar
    1.2.6
    aar

9. Conclusion

Dans ce didacticiel, nous avons exploréJDeferred et ses différents utilitaires.

Comme toujours, le code source complet est disponibleover on GitHub.