场景需求:
通过用户id获取用户的收藏列表
如果用户收藏列表为空,显示5条建议收藏记录
如果不为空显示5条用户收藏记录
阻塞式代码例子(回调地狱 callback hell)
userService.getFavorites(userId, new Callback<List<String>>() {
//1. 请求用户收藏列表 请求回调
public void onSuccess(List<String> list) { // 2
if (list.isEmpty()) { // 3
suggestionService.getSuggestions(new Callback<List<Favorite>>() { //4
//2. 用户收藏为空,请求5条推荐收藏 再次请求回调
public void onSuccess(List<Favorite> list) {
UiUtils.submitOnUiThread(() -> { // 5. 推送前台
list.stream()
.limit(5)
.forEach(uiList::show); // 6. 返回五条推荐并在ui线程中显示
});
}
public void onError(Throwable error) { //7
UiUtils.errorPopup(error);
}
});
} else {
list.stream() //8 获取收藏列表5个记录id
.limit(5)
.forEach(favId -> favoriteService.getDetails(favId,
new Callback<Favorite>() { //9 再次回调,返回内容详情
public void onSuccess(Favorite details) {
UiUtils.submitOnUiThread(() -> uiList.show(details));
}
public void onError(Throwable error) {
UiUtils.errorPopup(error);
}
}
));
}
}
public void onError(Throwable error) {
UiUtils.errorPopup(error);
}
});
分析
1.我们有基于回调的服务:一个回调接口,在异步进程成功时调用一个方法,在发生错误时调用一个方法。
2.第一个服务使用常用ID列表调用其回调。
3.如果列表为空,我们必须转到建议服务。
4.suggestionService向第二次回调提供一个列表<Favorite>。
5.因为我们处理的是UI,所以我们需要确保我们的消费代码在UI线程中运行。
6.我们使用Java 8流将处理的建议数量限制为五个,并在UI的图形列表中显示它们。
7.在每个级别,我们处理错误的方式都是一样的:我们在弹出窗口中显示错误。
8.回到收藏ID级别。如果服务返回完整列表,我们需要转到favoriteService以获取详细的收藏夹对象。因为我们只需要五个,所以我们首先流式处理ID列表,将其限制为五个。
9.再次回复。这一次,我们得到了一个完全成熟的最喜欢的对象,我们将其推送到UI线程内的UI。
缺点很明细,代码量大,不易跟踪,代码重复,对比响应式代码来实现相同功能。
反应式代码对比回调代码
userService.getFavorites(userId) //1.
.flatMap(favoriteService::getDetails) //2.
.switchIfEmpty(suggestionService.getSuggestions()) //3.
.take(5) //4.
.publishOn(UiUtils.uiThreadScheduler()) //5.
.subscribe(uiList::show, UiUtils::errorPopup); //6.
分析
1.我们根据用户ID获得收藏开始。
2.我们将这些对象转换为异步的常用对象(flatMap)。我们现在有了收藏的列表。
3.如果收藏夹流为空,我们将通过suggestionService切换到备用。
4.我们最多只对结果流中的五个元素感兴趣。
5.最后,我们希望处理UI线程中的每一条数据。
6.我们通过描述如何处理最终形式的数据(在UI列表中显示)以及发生错误时如何处理(显示弹出窗口)来触发响应式流
如果要确保在不到800毫秒的时间内检索到收藏的ID,或者如果需要更长时间,从缓存中获取它们,该怎么办?在基于回调的代码中,这是一项复杂的任务。在Reactor中,只需在链中添加一个超时操作符即可,如下所示:。
userService.getFavorites(userId)
.timeout(Duration.ofMillis(800)) // 1.
.onErrorResume(cacheService.cachedFavoritesFor(userId)) //2.
.flatMap(favoriteService::getDetails) //3.
.switchIfEmpty(suggestionService.getSuggestions())
.take(5)
.publishOn(UiUtils.uiThreadScheduler())
.subscribe(uiList::show, UiUtils::errorPopup);
分析
1.如果上述部件在超过800ms的时间内无任何辐射,则传播错误。
2.如果出现错误,请返回缓存服务。
3.链的其余部分与上一个示例类似。