JDeferred指南

2025/03/25

1. 概述

JDeferred是一个小型Java库(也支持Groovy),用于实现异步拓扑而无需编写样板代码。该框架的灵感来自Jquery的Promise/Ajax功能和Android的延迟对象模式。

在本教程中,我们将展示如何使用JDeferred及其不同的实用程序。

2. Maven依赖

通过将以下依赖添加到我们的pom.xml中,我们可以开始在任何应用程序中使用JDeferred:

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

可以在Maven中央仓库检查JDeferred项目的最新版本。

3. Promises

让我们看一个调用容易出错的同步REST API调用的简单用例,并根据API返回的数据执行一些任务。

在简单的JQuery中,上述场景可以通过以下方式解决:

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

类似地,JDeferred带有PromiseDeferred接口,它们在相应的对象上注册一个独立于线程的钩子,该钩子根据该对象的状态触发不同的可定制操作。

在这里,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,我们就无法对该对象执行reject操作

同样,一旦对象的状态变为REJECTED,我们就不能对该对象执行resolve或notify操作。任何违规都将导致IllegalStateException。

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. 延迟管理器

在实时场景中,我们需要处理多个Promise观察到的多个延迟对象。在这种情况下,单独管理多个Promise是相当困难的。

这就是JDeferred包含DeferredManager接口的原因,该接口为所有Promise创建了一个公共观察者。因此,使用这个公共观察者,我们可以为所有Promise创建通用操作:

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

我们还可以将带有自定义线程池的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的wait()方法来等待所有延迟任务完成

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

或者,我们可以使用Promise接口的waitSafely()方法来实现相同的目的。

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

尽管上述两种方法执行的操作几乎相同,但始终建议使用第二种方法,因为第二种方法不需要同步。

8. 安卓集成

使用Android Maven插件可以将JDeferred轻松地与Android应用程序集成。

对于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及其不同的实用程序。

Show Disqus Comments

Post Directory

扫码关注公众号:Taketoday
发送 290992
即可立即永久解锁本站全部文章