🚁 异步请求
Forest 在默认情况下发送的请求都是同步的,也就是说从发送请求开始,调用发送的线程就会被阻塞,直到请求的响应结果返回之后才会继续往下执行
但同样 Forest 也支持异步请求,允许您异步调用请求接口,而调用接口所在的线程不会被阻塞,可以继续往下做其它事情,这样就不会白白浪费机器的性能
# 同步/异步
Forest请求默认为同步的,可以通过async()
方法设置为异步请求
setAsync(boolean async) 设置是否异步
- 参数
aysnc
:true
异步,false
同步sync() 设置为同步
async() 设置为异步
// 构建同步 Get 请求
Forest.get("/");
// 构建异步 Get 请求
Forest.get("/").setAsync(true);
// 构建同步 Get 请求
Forest.get("/").setAsync(false);
// 构建异步 Get 请求
Forest.get("/").async();
// 构建同步 Get 请求
Forest.get("/").sync();
1
2
3
4
5
6
7
8
9
10
2
3
4
5
6
7
8
9
10
# 异步和回调函数
在发送异步请求的情况下,接口调用线程不会被阻塞,所以也不会等待请求的响应结果,那么如何去接受异步返回的数据呢?
第一种方法,就是使用回调函数
Forest.get("/foo") // 创建 GET 请求
.async() // 设置为异步
.onSuccess(((data, req, res) -> { // 设置 OnSuccess 回调函数 (在请求成功返回时调用)
// data 为响应成功后返回的反序列化过的数据
// req 为Forest请求对象,即 ForestRequest 类实例
// res 为Forest响应对象,即 ForestResponse 类实例
}))
.onError(((ex, req, res) -> { // 设置 OnError 回调函数 (在请求失败时调用)
// ex 为请求过程可能抛出的异常对象
// req 为Forest请求对象,即 ForestRequest 类实例
// res 为Forest响应对象,即 ForestResponse 类实例
}))
.execute(); // 发送请求
1
2
3
4
5
6
7
8
9
10
11
12
13
2
3
4
5
6
7
8
9
10
11
12
13
# Async/Await 风格
自 Ajax 流行开始,回调函数的异步处理方式就已经深入人心,这种方法虽好,但也有不少问题
比如回调地狱:很容易写出在A请求的回调函数中调用B请求,然后在B请求的回调函数中调用C请求,这样的套娃代码
另一个问题,就是执行回调函数所在的线程和调用接口的线程并非同一个线程,这就会引入并发和锁的问题,处理的时候需要谨慎对待
为了解决这些问题,Forest 引入了第二种方法
(从 1.5.27
版本后开始支持ForestFuture
)
String result = Forest.get("/foo") // 创建 GET 请求
.async() // 设置为异步
.executeAsFuture() // 发送请求,并返回 ForestFuture 对象
.await() // 阻塞线程并等待请求响应,成功响应后会返回 ForestResponse 对象
.get(String.class); // 调用 ForestResponse 的 get 方法进行转换数据并返回结果
1
2
3
4
5
2
3
4
5
这种方式相比回调函数方式,更为直观,也更类似同步方式
同时发送和接受多个异步请求时,更能体现它的好处
ForestFuture f1 = Forest.get("/foo") // 创建第一个 GET 请求
.async() // 设置为异步
.addQuery("id", 0) // 设置 Query 参数
.executeAsFuture(); // 发送请求并返回 ForestFuture 对象
ForestFuture f2 = Forest.get("/bar") // 创建第二个 GET 请求
.async() // 设置为异步
.addQuery("id", 1) // 设置 Query 参数
.executeAsFuture(); // 发送请求并返回 ForestFuture 对象
// 在这里并不阻塞线程,可以做些其它事情
doSomething();
// Forest.await 方法会阻塞线程并同时等待多个请求
// 这里同时等待 f1 和 f2 这两个请求
// 只有当这两个请求都完成时,会返回 ForestResponse 对象列表
// 并继续往下执行
Forest.await(f1, f2).forEach(res -> { // 遍历每个 ForestResponse 对象
String result = res.get(String.class); // 从 ForestResponse 中取出数据
});
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
批量发送和等待多个异步请求
// new 一个 ForestFuture 对象列表
List<ForestFuture> futures = new LinkedList<>();
for (int i = 0; i < 100; i++) { // 循环100次
futures.add(Forest.get("/data") // 创建请求,并添加到 futures 列表中
.async() // 设置为异步
.addQuery("id", i) // 设置 Query 参数
.executeAsFuture()); // 发送请求,返回 ForestFuture 对象
}
Forest.await(futures).forEach(res -> { // 等待这100个请求,然后进行遍历
String result = res.get(String.class); // 从 ForestResponse 中取出数据
});
1
2
3
4
5
6
7
8
9
10
11
12
2
3
4
5
6
7
8
9
10
11
12
更简化的方式
// new 一个 ForestFuture 对象列表
List<ForestFuture> futures = new LinkedList<>();
for (int i = 0; i < 100; i++) { // 循环100次
futures.add(Forest.get("/data") // 创建请求,并添加到 futures 列表中
.async() // 设置为异步
.addQuery("id", i) // 设置 Query 参数
.executeAsFuture()); // 发送请求,返回 ForestFuture 对象
}
Forest.await(futures, res -> { // 等待这100个请求,然后进行遍历
String result = res.get(String.class); // 从 ForestResponse 中取出数据
});
1
2
3
4
5
6
7
8
9
10
11
12
2
3
4
5
6
7
8
9
10
11
12
# Kotlin 协程
1.5.27
版本开始 Forest 支持 Kotlin 的协程,而默认情况下并不开启,开启方式请参见《开启 Kotlin 协程》
除了这种全局配置外,Forest 也允许通过编程式接口设置某个具体请求的异步模式
Forest.get("/foo")
.async()
// 设置为 Kotlin 协程
.asyncMode(ForestAsyncMode.KOTLIN_COROUTINE);
1
2
3
4
2
3
4
也可以切换回默认的线程池模式
Forest.get("/foo")
.async()
// 设置为 Kotlin 协程
.asyncMode(ForestAsyncMode.PLATFORM);
1
2
3
4
2
3
4
帮助我们改善此文档 (opens new window)
上次更新: 2023/06/19, 17:37:19