前言
前情提要,这个类其实是一个一时兴起的玩具,或者可以成为一个有意思的美丽小废物。
有一天在写js的时候,用async/await就能简单实现异步操作,一个promise就能优雅地处理异步流程,那是不是java也可以?
虽然completablefuture已经很全面了,但是写起来还是有点麻烦、冗长。
能不能在Java里也搞个类似的东西,让异步代码写起来更轻松呢?
心动不如立即行动,开整!

正文
💡 最初的痛点
我们平时是怎么处理Java中的异步操作的?是不是到处都是这样的代码:
java
CompletableFuture<String> future = CompletableFuture.supplyAsync(() -> {
// 一些耗时操作
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
return "操作结果";
});
future.thenAccept(result -> {
System.out.println("获得结果: " + result);
}).exceptionally(ex -> {
System.err.println("发生错误: " + ex.getMessage());
return null;
});
每次都得写这种冗长的代码,链式调用虽然比回调嵌套好,但还是不够直观,尤其是当你需要在异步操作之间传递数据时,代码会变得更加复杂。
🚀 灵感来源:async/await
有次写js时,看着async/await写异步代码这么爽,就在想:Java能不能也整一个这么方便的东西?
javascript
async function fetchData() {
try {
const response = await fetch('https://api.example.com/data');
const data = await response.json();
return data;
} catch (error) {
console.error('获取数据失败:', error);
}
}
看看上面的JavaScript代码,是不是非常清晰?异步操作看起来就像同步代码一样,没有回调,没有链式调用,简洁明了。
那么,我们能否在Java中也实现类似的效果呢?
但是受限于Java的特性,我们没办法去百分百还原,但是也可以通过 import static
和函数调用来尽可能的模仿。
如果能这样写Java代码,那岂不是很优雅:
java
// 定义异步任务
Promise<String> promise = async(() -> {
// 耗时操作
Thread.sleep(1000);
return "异步操作完成";
});
// 等待结果,就像JavaScript的await
String result = await(promise);
System.out.println(result);
简洁、直观,没有嵌套的回调,没有复杂的链式调用,这就是我想要的美丽小废物!
🚀 开整
实现这个想法其实并不复杂。我们需要两个核心组件:
Promise
:表示异步操作的结果async
和await
方法:用于创建和等待异步任务
核心思路是:
- 使用虚拟线程执行异步任务
- 用Promise封装异步结果
- 提供await方法阻塞当前线程直到Promise完成
至于为啥我要用虚拟线程,因为最近的项目都是JDK21,虚拟线程真的太香了,强烈建议使用。
java
public static <T> Promise<T> async(Task<T> task) {
Promise<T> promise = new Promise<>();
// 使用虚拟线程来异步执行任务
Thread.ofVirtual().start(() -> {
try {
// 如果任务成功完成,则用结果解析promise
promise.resolve(task.run());
} catch (Throwable e) {
// 如果任务抛出异常,则拒绝promise并传递异常
promise.reject(e);
}
});
return promise;
}
public static <T> T await(Promise<T> promise) {
return promise.await();
}
Promise类内部使用CompletableFuture实现,但对外提供了更简单的API:
java
public static class Promise<T> {
private final CompletableFuture<T> future = new CompletableFuture<>();
public void resolve(T value) {
future.complete(value);
}
public void reject(Throwable ex) {
future.completeExceptionally(ex);
}
public T await() {
return future.join();
}
}
是不是非常简单。
其实就是对 CompletableFuture
做了个包装,实现了一点点有趣的能力。
🪣 完整代码
这里贴上完整的代码。
也可以直接去Github仓库里看,有丰富的使用示例和单元测试,这是跳转地址(PS.还没整理好,整理好之后就贴上来)。
java
import java.util.concurrent.CompletableFuture;
/**
* 一个提供类似JavaScript async/await功能的Java工具类。
* 该实现使用Java虚拟线程来执行异步任务,
* 并提供简单的Promise-based API来处理异步操作。
*
* <p>使用示例:</p>
* <pre>{@code
* // 定义一个异步任务
* var promise = Async.async(() -> {
* // 一些耗时操作
* Thread.sleep(1000);
* return "异步操作的结果";
* });
*
* // 等待结果
* String result = Async.await(promise);
* }</pre>
*
* @author easyboot
*/
public class Async {
/**
* 使用虚拟线程异步执行任务。
*
* @param <T> 任务返回结果的类型
* @param task 要异步执行的任务
* @return 一个Promise对象,将来会被解析为任务的结果或被拒绝并包含异常
*/
public static <T> Promise<T> async(Task<T> task) {
Promise<T> promise = new Promise<>();
// 使用虚拟线程来异步执行任务
Thread.ofVirtual().start(() -> {
try {
// 如果任务成功完成,则用结果解析promise
promise.resolve(task.run());
} catch (Throwable e) {
// 如果任务抛出异常,则拒绝promise并传递异常
promise.reject(e);
}
});
return promise;
}
/**
* 等待promise被解析并返回其结果。
* 此方法会阻塞当前线程直到promise被履行。
* 如果promise被拒绝,异常将被抛出。
*
* @param <T> 结果的类型
* @param promise 要等待的promise
* @return promise的解析值
* @throws RuntimeException 如果promise被异常拒绝
*/
public static <T> T await(Promise<T> promise) {
return promise.await();
}
/**
* Promise类表示异步操作的最终完成。
* 类似于JavaScript的Promise,它可以处于三种状态之一:
* 等待中(pending)、已完成(fulfilled/resolved)或已拒绝(rejected)。
*
* @param <T> promise将被解析的值的类型
*/
public static class Promise<T> {
// 使用CompletableFuture来实现Promise功能
private final CompletableFuture<T> future = new CompletableFuture<>();
/**
* 用给定的值解析promise。
*
* @param value 用于解析promise的值
*/
public void resolve(T value) {
future.complete(value);
}
/**
* 用给定的异常拒绝promise。
*
* @param ex 用于拒绝promise的异常
*/
public void reject(Throwable ex) {
future.completeExceptionally(ex);
}
/**
* 等待promise被解析并返回其值。
* 此方法会阻塞当前线程直到promise被履行。
*
* @return promise的解析值
* @throws RuntimeException 如果promise被异常拒绝
*/
public T await() {
return future.join();
}
/**
* 将此Promise转换为CompletableFuture。
*
* @return 支持此Promise的CompletableFuture
*/
public CompletableFuture<T> toFuture() {
return future;
}
}
/**
* 表示可以异步运行的任务的函数式接口。
*
* @param <T> 任务返回的结果类型
*/
@FunctionalInterface
public interface Task<T> {
/**
* 执行任务并返回结果。
*
* @return 任务的结果
* @throws Exception 如果执行过程中发生错误
*/
T run() throws Exception;
}
}
🐬 使用示例
下面是一个简单的使用示例,展示了如何使用Async类进行异步编程:
java
// 创建一个异步任务
Promise<String> promise = async(() -> {
System.out.println("异步任务开始执行...");
Thread.sleep(2000); // 模拟耗时操作
System.out.println("异步任务执行完成");
return "操作结果";
});
System.out.println("等待异步任务完成...");
String result = await(promise); // 阻塞直到任务完成
System.out.println("获得结果: " + result);
输出结果:
erlang
异步任务开始执行...
等待异步任务完成...
异步任务执行完成
获得结果: 操作结果
🚀 复杂场景:串行和并行执行
这个简单的工具类也能处理更复杂的异步场景:
串行执行多个异步任务:
java
Promise<String> promise1 = async(() -> {
Thread.sleep(1000);
return "第一步";
});
String step1Result = await(promise1);
Promise<String> promise2 = async(() -> {
Thread.sleep(1000);
return step1Result + " -> 第二步";
});
String finalResult = await(promise2);
System.out.println(finalResult); // 输出: 第一步 -> 第二步
并行执行多个异步任务:
java
Promise<String> promise1 = async(() -> {
Thread.sleep(1000);
return "任务1结果";
});
Promise<String> promise2 = async(() -> {
Thread.sleep(1000);
return "任务2结果";
});
// 两个任务并行执行,然后等待所有结果
String result1 = await(promise1);
String result2 = await(promise2);
System.out.println(result1 + " + " + result2);
🤔 改进建议
当然,这个类还有很大的改进空间,我简单列几个,列位看官可以根据自己的真实场景再逐步进行优化:
- 添加超时机制,避免无限等待
- 支持取消操作
- 添加Promise组合操作,如all、race等
- 使用线程池而非每次创建新线程,以支持JDK 8-17
- 添加更多的错误处理机制
- 支持异步操作的进度通知
革命尚未成功,同志仍需努力。
总结
🤌 一点点经验
先来点经验总结,仁者见仁,智者见智:
- 借鉴其他语言的优秀特性可以带来新的编程体验
- 虚拟线程是Java异步编程的一大进步
- 简单的封装可以大大提升代码的可读性和维护性
- 异步编程的核心是关注点分离,让代码结构更清晰
- 即使是玩具项目,也能带来实际的启发和价值
⚖️ 写在最后
这个小小的玩具类并不复杂,技术含量也不高,核心代码不过才几十行,但却能让我们在Java中也能感受到类似 JS 中 async/await的体验。
这就是开发的乐趣所在:用简单的代码解决实际的问题,让开发体验更加愉悦。
而这大概也是写代码的意义:不是为了炫技,而是为了解决问题,让复杂的事情变得简单。
就像一把好用的工具,真正的价值在于它能让使用者专注于要解决的问题本身,而不是工具本身。
有时候,最好的代码就是那种让你忘记它存在的代码。
