Guia para JDeferred

Guia para JDeferred

1. Visão geral

JDeferred é uma pequena bibliotecaJava (também suportaGroovy) usada para implementar a topologia assíncrona sem escrever código clichê. Esta estrutura é inspirada no recursoJquery’s Promise/Ajax e no padrãoAndroid’s Deferred Object.

Neste tutorial, mostraremos como usarJDeferrede seus diferentes utilitários.

2. Dependência do Maven

Podemos começar a usarJDeferred em qualquer aplicativo adicionando a seguinte dependência em nossopom.xml:


    org.jdeferred
    jdeferred-core
    1.2.6

Podemos verificar a última versão do projetoJDeferred noCentral Maven Repository.

3. Promessas

Vamos dar uma olhada em um caso de uso simples de invocar uma chamadaRESTAPI síncrona sujeita a erros e realizar algumas tarefas com base nos dados retornados pela API.

No JQuery simples, o cenário acima pode ser tratado da seguinte maneira:

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

Da mesma forma,JDeferred vem com as interfacesPromiseeDeferred que registram um gancho independente de thread no objeto correspondente que dispara diferentes ações personalizáveis ​​com base no status desse objeto.

Aqui,Deferred atua como o gatilho ePromise atua como o observador.

Podemos criar facilmente esse tipo de fluxo de trabalho assíncrono:

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");

Aqui, cada método tem semântica diferente:

  • done() - dispara apenas quando as ações pendentes no objeto adiado é / são concluídas com sucesso

  • fail() - dispara enquanto alguma exceção é levantada durante a execução de ação (s) pendente (s) no objeto adiado

  • progress() - dispara assim que as ações pendentes no objeto adiado são / começam a executar

  • always() - dispara independentemente do estado do objeto adiado

Por padrão, o status de um objeto adiado pode serPENDING/REJECTED/RESOLVED. Podemos verificar o status usando o métododeferred.state().

O ponto a ser observado aqui é queonce a deferred object’s status is changed to RESOLVED, we can’t perform reject operation on that object.

Da mesma forma, uma vez que o status do objeto é alterado paraREJECTED,, não podemos realizar a operaçãoresolve ounotify nesse objeto. Qualquer violação resultará emIllegalStateExeption.

4. Filtros

Antes de recuperar o resultado final, podemos realizar a filtragem no objeto diferido comDoneFilter.

Assim que a filtragem for concluída, obteremos o objeto deferido 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. Tubos

Semelhante ao filtro,JDeferred oferece a interfaceDonePipe para executar ações de pós-filtragem sofisticadas assim que as ações pendentes do objeto adiado forem resolvidas.

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;
}

Aqui, com base no valor do resultado real, levantamos uma exceção para rejeitar o resultado.

6. Gerente Adiado

Em um cenário em tempo real, precisamos lidar com os vários objetos adiados observados por várias promessas. Nesse cenário, é muito difícil gerenciar várias promessas separadamente.

É por isso queJDeferred vem com interfaceDeferredManager, que cria um observador comum para todas as promessas. Portanto, usando esse observador comum, podemos criar ações comuns para todas as promessas:

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");

Também podemos atribuirExecutorService com um pool de thread personalizado paraDeferredManager:

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

Na verdade, podemos ignorar completamente o uso dePromisee definir diretamente a interfaceCallable para concluir a tarefa:

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

7. Thread-Safe Action

Embora, na maioria das vezes, tenhamos de lidar com o fluxo de trabalho assíncrono, algumas vezes precisamos esperar pelos resultados de todas as tarefas paralelas.

Nesse tipo de cenário, só podemos usar o métodoObject'swait() parawait 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");

Alternativamente, podemos usar o métodowaitSafely() da interfacePromise para conseguir o mesmo.

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

Embora ambos os métodos acima executem praticamente a mesma coisa, é sempre aconselhável usar o segundo, uma vez que o segundo procedimento não requer sincronização.

8. Integração Android

JDeferred pode ser facilmente integrado com aplicativos Android usando o plugin Android Maven.

Para compilar APKLIB, precisamos adicionar a seguinte dependência nopom.xml:


    org.jdeferred
    jdeferred-android
    1.2.6
    apklib

Para a compilação deAAR, precisamos adicionar a seguinte dependência empom.xml:


    org.jdeferred
    jdeferred-android-aar
    1.2.6
    aar

9. Conclusão

Neste tutorial, exploramos sobreJDeferred e seus diferentes utilitários.

Como sempre, o código-fonte completo está disponívelover on GitHub.