Перейти к основному содержимому

Руководство по JDeferred

· 5 мин. чтения

1. Обзор

JDeferred — это небольшая библиотека Java (также поддерживает Groovy ), используемая для реализации асинхронной топологии без написания шаблонного кода. Этот фреймворк основан на функции Promise/Ajax Jquery и шаблоне Deferred Object в Android .

В этом руководстве мы покажем, как использовать JDeferred и его различные утилиты.

2. Зависимость от Maven

Мы можем начать использовать JDeferred в любом приложении, добавив следующую зависимость в наш pom.xml:

<dependency>
<groupId>org.jdeferred</groupId>
<artifactId>jdeferred-core</artifactId>
<version>1.2.6</version>
</dependency>

Мы можем проверить последнюю версию проекта JDeferred в Центральном репозитории Maven .

3. Обещания

Давайте рассмотрим простой пример использования подверженного ошибкам синхронного вызова REST API и выполним некоторую задачу на основе данных, возвращаемых API.

В простом JQuery описанный выше сценарий можно решить следующим образом:

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

Точно так же JDeferred поставляется с интерфейсами Promise и Deferred , которые регистрируют независимый от потока хук для соответствующего объекта, который запускает различные настраиваемые действия в зависимости от состояния этого объекта.

Здесь Deferred действует как триггер, а Promise действует как наблюдатель.

Мы можем легко создать этот тип асинхронного рабочего процесса:

Deferred<String, String, String> deferred
= new DeferredObject<>();
Promise<String, String, String> 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");

Здесь каждый метод имеет разную семантику:

  • done () — срабатывает только тогда, когда ожидающие действия над отложенным объектом успешно завершены.
  • fail () — срабатывает, когда возникает какое-то исключение при выполнении ожидающих действий над отложенным объектом.
  • progress () — срабатывает, как только ожидающие действия над отложенным объектом начинают выполняться
  • always() — срабатывает вне зависимости от состояния отложенного объекта.

По умолчанию статус отложенного объекта может быть PENDING/REJECTED/RESOLVED . Мы можем проверить статус, используя метод deferred.state() .

Здесь следует отметить, что как только статус отложенного объекта изменится на RESOLVED, мы не сможем выполнить операцию отклонения этого объекта.

Точно так же, как только статус объекта изменится на REJECTED, мы не сможем выполнить операцию разрешения или уведомления для этого объекта. Любое нарушение приведет к IllegalStateExeption .

4. Фильтры

Прежде чем получить окончательный результат, мы можем выполнить фильтрацию отложенного объекта с помощью DoneFilter .

После завершения фильтрации мы получим потокобезопасный отложенный объект:

private static String modifiedMsg;

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

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

d.resolve(msg);
return modifiedMsg;
}

5. Трубы

Подобно фильтру, JDeferred предлагает интерфейс DonePipe для выполнения сложных действий пост-фильтрации после разрешения отложенных ожидающих действий объекта.

public enum Result { 
SUCCESS, FAILURE
};

private static Result status;

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

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

d.resolve(num);
return status;
}

Здесь, основываясь на значении фактического результата, мы вызвали исключение, чтобы отклонить результат.

6. Отложенный менеджер

В сценарии реального времени нам нужно иметь дело с несколькими отложенными объектами, наблюдаемыми несколькими промисами. В этом сценарии довольно сложно управлять несколькими промисами по отдельности.

Вот почему JDeferred поставляется с интерфейсом DeferredManager , который создает общего наблюдателя для всех промисов. Следовательно, используя этот общий наблюдатель, мы можем создать общие действия для всех промисов:

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

Мы также можем назначить ExecutorService с настраиваемым пулом потоков для DeferredManager :

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

На самом деле, мы можем полностью игнорировать использование Promise и можем напрямую определить интерфейс Callable для выполнения задачи:

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

7. Поточно-безопасное действие

Хотя большую часть времени нам приходится иметь дело с асинхронным рабочим процессом, иногда нам нужно ждать результатов всех параллельных задач.

В этом типе сценария мы можем использовать только метод wait() Object для ожидания завершения всех отложенных задач : `` ****

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

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

deferred.resolve("Hello ForEach");

В качестве альтернативы мы можем использовать метод waitSafely() интерфейса Promise , чтобы добиться того же. ``

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

Хотя оба вышеупомянутых метода выполняют почти одно и то же, всегда рекомендуется использовать второй, поскольку вторая процедура не требует синхронизации.

8. Интеграция с Android

JDeferred можно легко интегрировать с приложениями Android с помощью подключаемого модуля Android Maven.

Для сборки APKLIB нам нужно добавить следующую зависимость в pom.xml :

<dependency>
<groupId>org.jdeferred</groupId>
<artifactId>jdeferred-android</artifactId>
<version>1.2.6</version>
<type>apklib</type>
</dependency>

Для сборки AAR нам нужно добавить следующую зависимость в pom.xml :

<dependency>
<groupId>org.jdeferred</groupId>
<artifactId>jdeferred-android-aar</artifactId>
<version>1.2.6</version>
<type>aar</type>
</dependency>

9. Заключение

В этом руководстве мы рассмотрели JDeferred и его различные утилиты.

Как всегда, полный исходный код доступен на GitHub .