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.