跳到主要内容

异步和同步(async/await)

响应式编程是一种编程范式, 专注于数据流和变化传播. 通过自动传播数据变化和处理异步操作, 提高系统的响应性和可扩展性. 响应式编程的核心思想是构建系统, 使其能够自动响应输入数据的变化, 并通过非阻塞方式处理异步事件

传统的编程思维通常是命令式的, 同步的. 在这种模式下代码按顺序执行, 程序的状态和流向易于理解和控制. 然而响应式编程是基于数据流和变化传播的异步编程范式, 需要开发者习惯于处理非阻塞和并发的操作

为了降低响应式编程的使用门槛, Nozdormu 参考 JavaScript, C# 等现代编程语言, 通过简单的接口和 API 提供 asyncawait 特性来简化异步代码的编写和维护. 通过代码静态分析, 自动转译同步代码为异步代码, 降低异步编程的心智负担

安装

添加依赖

repositories {
mavenCentral()
jcenter()
}

dependencies {
implementation 'org.graphoenix:nozdormu-inject:0.1.0'
implementation 'org.graphoenix:nozdormu-async:0.1.0'

annotationProcessor 'org.graphoenix:nozdormu-inject:0.1.0'
annotationProcessor 'org.graphoenix:nozdormu-async:0.1.0'

// ...
}

定义与调用

通过实现 Asyncable 接口定义异步类, 异步类中可以使用 @Async 注解与 await 方法来同步异步方法, 通过 asyncInvoke 方法来异步执行方法和获取执行结果

  1. 例: 通过用户名自动调用异步方法生成邮箱, 通过 await(buildEmail(name)) 来同步异步方法, 通过 @Async 声明方法为异步执行
UserInfo.java
import io.nozdormu.spi.async.Async;
import io.nozdormu.spi.async.Asyncable;
import jakarta.enterprise.context.ApplicationScoped;
import reactor.core.publisher.Flux;
import reactor.core.publisher.Mono;

@ApplicationScoped
public class UserInfo implements Asyncable {

public Mono<String> buildEmail(String name) {
return Mono.just(name + "@nozdormu.com");
}

public Flux<Integer> buildPassword(String email, int size) {
return Flux.range(0, size)
.map(index -> (index + 1) * email.length());
}

@Async
public User getUser(String name) {
User user = new User();
String email = await(buildEmail(name));
user.setName(name);
user.setEmail(email);
return user;
}
}

测试

AsyncTest.java
import io.nozdormu.async.test.beans.User;
import io.nozdormu.async.test.beans.UserInfo;
import io.nozdormu.spi.context.BeanContext;
import org.junit.jupiter.api.Test;
import reactor.core.publisher.Mono;
import reactor.test.StepVerifier;

import static org.junit.jupiter.api.Assertions.assertEquals;

public class AsyncTest {

@Test
void testUser() {
UserInfo userInfo = BeanContext.get(UserInfo.class);
Mono<User> userMono = userInfo.asyncInvoke("getUser", "nozdormu");
StepVerifier.create(userMono)
.assertNext(user -> assertEquals(user.getEmail(), "nozdormu@nozdormu.com"))
.expectComplete()
.verify();
}
}
  1. 例: 通过模拟用户注册展示多个异步类之间的方法调用, UserService 调用 UserInfo 中的异步方法
UserService.java
import io.nozdormu.spi.async.Async;
import io.nozdormu.spi.async.Asyncable;
import jakarta.enterprise.context.ApplicationScoped;
import jakarta.inject.Inject;

import java.util.List;
import java.util.stream.Collectors;

@ApplicationScoped
public class UserService implements Asyncable {

private final UserInfo userInfo;

@Inject
public UserService(UserInfo userInfo) {
this.userInfo = userInfo;
}

@Async
public User register(String name, int age) {
User user = await(userInfo.getUser(name));
user.setAge(age);
return user;
}

@Async
public String genPassword(User user) {
User registedUser = await(userInfo.getUser(user.getName()));
List<Integer> passwords = await(userInfo.buildPassword(registedUser.getEmail(), registedUser.getName().length()));
return passwords.stream().map(Object::toString).collect(Collectors.joining(""));
}
}

测试

AsyncTest.java
import io.nozdormu.async.test.beans.User;
import io.nozdormu.async.test.beans.UserService;
import io.nozdormu.spi.context.BeanContext;
import org.junit.jupiter.api.Test;
import reactor.core.publisher.Mono;
import reactor.test.StepVerifier;

import java.util.stream.Collectors;
import java.util.stream.IntStream;

import static org.junit.jupiter.api.Assertions.assertEquals;

public class AsyncTest {

@Test
void testUser() {
UserService userService = BeanContext.get(UserService.class);
Mono<User> userMono = userService.asyncInvoke("register", "nozdormu", 6);
StepVerifier.create(userMono)
.assertNext(user -> assertEquals(user.getEmail(), "nozdormu@nozdormu.com"))
.expectComplete()
.verify();

String name = "kai";
String email = "kai@nozdormu.com";
String target = IntStream.range(0, name.length())
.mapToObj(index -> "" + (index + 1) * email.length())
.collect(Collectors.joining(""));
User user = new User();
user.setName(name);

Mono<String> genPassword = userService.asyncInvoke("genPassword", user);
StepVerifier.create(genPassword)
.assertNext(password -> assertEquals(password, target))
.expectComplete()
.verify();
}
}

异步 API

Asyncable

方法参数返回值说明
T await(T methodInvoke)标记为 @Async 的方法T同步执行结果
T await(Mono<T> methodInvoke)Mono 类型的异步方法T同步执行结果
List<T> await(Flux<T> methodInvoke)Flux 类型的异步方法List<T>聚合为集合后同步执行结果
Mono<T> asyncInvoke(String methodName, Object... parameters)方法名,方法参数...Mono<T>执行标记为 @Async 的方法并异步返回结果

注解说明

注解目标说明
io.nozdormu.spi.async.Async方法标记方法为异步执行, 使用 defaultIfEmpty 字段配置默认返回值

本节示例

https://github.com/doukai/nozdormu/tree/main/nozdormu-async/src/test/java/io/nozdormu/async/test