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

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

1. обзор

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

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

2. Maven Dependency

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


    org.jdeferred
    jdeferred-core
    1.2.6

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

3. обещания

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

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

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

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

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

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

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

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

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

  • fail() - срабатывает, когда возникает какое-то исключение при выполнении отложенного действия / с для отложенного объекта

  • progress() - срабатывает, как только ожидающие действия над отложенным объектом запускаются / начинают выполняться

  • always() - срабатывает независимо от состояния отложенного объекта

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

Здесь следует отметить, чтоonce a deferred object’s status is changed to RESOLVED, we can’t perform reject operation on that object.

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

4. фильтры

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

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

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. трубы

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

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

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

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

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

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

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

Мы также можем назначить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. Поточно-безопасное действие

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

В этом типе сценария мы можем использовать только методObject‘swait() дляwait 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");

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

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

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

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

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

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


    org.jdeferred
    jdeferred-android
    1.2.6
    apklib

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


    org.jdeferred
    jdeferred-android-aar
    1.2.6
    aar

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

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

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