Stateful Future Transformation
As an async programming pattern, Future has been popular with many of our programmers across a wide range of languages. Loosely speaking, Future is a wrapper around a value which will be available at some point in the future. Strictly speaking, Future is a monad which supports the following 3 operations:
unit :: T -> Future<T>
map :: (T -> R) -> (Future<T> -> Future<R>)
flatMap :: (T -> Future<R>) -> (Future<T> -> Future<R>)
When holding a future, we know the type of the value, we can register callbacks which will be called when the future is done. But callbacks are not the recommended way to deal with futures, the point of Future pattern is to avoid callbacks and in favor of future transformation. By properly using future transformation, we can make our async code look like sequential code, the callbacks are hidden from us by the futures.
Here is an example, say there are 2 async RPCs. One takes a user ID and returns a future of a list of the user's new message header (ID and title), the other takes a message ID and returns its body.
// RPC 1: Gets a list of new message (headers) of a user.
Future<NewMessagesResponse> getNewMessages(UserId userId);
// RPC 2: Gets the full message for a message ID.
Future<Message> getMessage(MessageId messageId);
// Data structures.
class Message {
class Header {
MessageId id;
String title;
}
class Body {
...
}
Header header;
Body body;
}
class NewMessagesResponse {
List<MessageHeaders> headers;
}
Your task is that, given a user ID and a keyword, get the user's new messages whose title contains the keyword. With future transformation, the code may look like:
// Gets the future of a list of messages for a user, whose titles contains a given keyword.
Future<List<Message>> getNewMessages(UserId userId, String keyword) {
Future<NewMessagesResponse> newMessagesFuture = getNewMessages(userId);
Future<List<MessageId>> interestingIdsFuture = filter(newMessagesFuture, keyword);
Future<List<Message>> messagesFuture = getMessages(interestingIdsFuture);
Return messages;
}
The structure of the code is similar to what we do with synchronous code:
List<Message> getNewMessages(UserId userId, String keyword) {
NewMessagesResponse newMessages = getNewMessages(userId);
List<MessageId> interestingIds = filter(newMessages, keyword);
List<Message> messages = getMessages(interestingIds);
Return messages;
}
The async and sync functions are isomorphic, there is a correspondence in their code structure. But their runtime behaviors are different, one happens asynchronously, one happens synchronously.
Now here comes the real challenge. What if we change the RPC a bit, say there may be too many new messages that it has to return messages page by page, each response may contain an optional next page token indicating there are more pages.
// RPC 1: Gets one page of the new message (headers) of a user. The page number is denoted by a pageToken.
Future<NewMessagesResponse> getNewMessageHeaders(UserId userId, String pageToken)
class NewMessagesResponse {
List<MessageHeaders> messageHeaders;
String nextPageToken; // Non-empty nextPageToken indicates there are more pages.
}
Your task remains the same, write a function which takes a user ID and a keyword, return a list of the user's new messages whose titles contain the keyword.
Future<List<Message>> getNewMessages(UserId userId, String keyword) {
//TODO
}
The difficulty lies with that in regular future transformations we have fixed number of steps, we can simply chain them together sequentially, then we get one future of the final result; but now because of pagination, the number of steps is not nondeterministic, how can we chain them together?
For synchronous code, we may use a loop like:
List<Message> getNewMessages(UserId userId, String keyword) {
List<MessageId> interestingMessages = new ArrayList<>();
String pageToken = "";
do {
NewMessagesResponse newMessages = getNewMessages(userId, pageToken);
List<MessageId> interestingIds = filter(newMessages, keyword);
allNewMessages.addAll(newMessages.headers);
pageToken = newMessages.nextPageToken;
} while (!isEmpty(pageToken));
}
But unfortunately loop is applicable to futures. How can we get one future for all the pages? Recursion comes to rescue. This is what I call *Stateful Future Transformation*.
class State {
UserId userId;
String keyword;
int pageIndex;
String pageToken;
List<MessageId> buffer;
}
Future<State> getInterestingMessages(Future<State> stateFuture) {
return Future.transform(
stateFuture, (State state) -> {
if (state.pageIndex == 0 || !isEmpty(state.pageToken)) {
// Final state.
return Future.immediate(state);
} else {
// Intermediate state.
Future<NewMessagesResponse> newMessagesFuture =
getNewMessages(state.userId, state.pageToken);
return Future.transform(newMessagesFuture, newMessages -> {
state.pageIndex++;
state.pageToken = newMessages.nextPageToken;
state.buffer.addAll(filter(newMessages, state.keyword);
});
}
});
}
Future<State> getInterestingMessages(UserId userId, String keyword) {
State initialState = new State(userId, keyword, 0, "", new ArrayList());
Future<State> initialStateFuture = Future.immediate(initialState);
return getInterestingMessages(initialStateFuture);
}
The code above can be refactored into a general stateful future transformation function:
// Transforms the future of an initial state future into the future of its final state.
Future<StateT> transform(
Future<StateT> stateFuture,
Function<StateT, Boolean> isFinalState,
Function<StateT, Future<StateT>> getNextState) {
return Future.transform(
stateFuture,
(StateT state) -> {
return isFinalState.apply(state)
? Future.immediate(state)
: transform(getNextState.appy(state));
}
});
}
Stateful Future Transformation的更多相关文章
- Isomorphic JavaScript: The Future of Web Apps
Isomorphic JavaScript: The Future of Web Apps At Airbnb, we’ve learned a lot over the past few years ...
- Spark Streaming揭秘 Day24 Transformation和action图解
Spark Streaming揭秘 Day24 Transformation和action图解 今天我们进入SparkStreaming的数据处理,谈一下两个重要的操作Transfromation和a ...
- Future Works on P4
Future Works on P4 P4 and NV: MPvisor, Hyper4, HyperV, Flex4 P4 and NFV P4 and Network Cache P4 and ...
- explain the past and guide the future 好的代码的标准:解释过去,指引未来;
好的代码的标准:解释过去,指引未来: Design philosophies | Django documentation | Django https://docs.djangoproject.co ...
- .Netcore 2.0 Ocelot Api网关教程(10)- Headers Transformation
本文介绍Ocelot中的请求头传递(Headers Transformation),其可以改变上游request传递给下游/下游response传递给上游的header. 1.修改ValuesCont ...
- 使用 Vert.X Future/Promise 编写异步代码
Future 和 Promise 是 Vert.X 4.0中的重要角色,贯穿了整个 Vert.X 框架.掌握 Future/Promise 的用法,是用好 Vert.X.编写高质量异步代码的基础.本文 ...
- 面向未来的友好设计:Future Friendly
一年前翻译了本文的一部分,最近终于翻译完成.虽然此设计思想的提出已经好几年了,但是还是觉得应该在国内推广一下,让大家知道“内容策略”,“移动优先”,“响应式设计”,“原子设计”等设计思想和技术的根源. ...
- 线程笔记:Future模式
线程技术可以让我们的程序同时做多件事情,线程的工作模式有很多,常见的一种模式就是处理网站的并发,今天我来说说线程另一种很常见的模式,这个模式和前端里的ajax类似:浏览器一个主线程执行javascri ...
- 第二篇 Entity Framework Plus 之 Query Future
从性能的角度出发,能够减少 增,删,改,查,跟数据库打交道次数,肯定是对性能会有所提升的(这里单纯是数据库部分). 今天主要怎样减少Entity Framework查询跟数据库打交道的次数,来提高查询 ...
随机推荐
- easyui 传递参数报错(错误:uncaught SyntaxError: Unexpected identifier)
转自:https://www.cnblogs.com/javaboy2018/p/8733585.html 代码: 按钮事件: function formatOper(val, row, index) ...
- 分布式事务Hmily TCC源码--学习整合
一.什么是分布式事务 分布式事务是指事务的参与者.支持事务的服务器.资源服务器以及事务管理器分别位于不同的分布式系统的不同节点上, 本质上来说,分布式事务是为了保证不同数据库的数据一致性 TCC事务主 ...
- linux下搭建Jenkins环境
前提:Tomcat.jdk已安装并配置成功,具体安装和配置可参考我的其他随笔,在此不再详述 1.官网下载Jenkins最新war包,jenkins.war 2.进入Tomcat安装目录,创建Jenki ...
- API Test WebApiTestClient工具安装及使用
一.guget安装: 1.解决方案右键-管理解决方案的nuget程序包打开如下图: 搜索WebApiTestClient,然后选择查询出的项目,右边点击安装即可: 2.安装会有如下图提示: 确定即 ...
- hbase-运维命令
hbase 问题 不一致问题 meta表不一致问题 hdfs文件不一致问题 hbase hbck hbase hbck 用于检测和修改hbase底层文件问题.检测像master,region serv ...
- Python语言学习之C++调用python
C++调用python 在C/C++中嵌入Python,可以使用Python提供的强大功能,通过嵌入Python可以替代动态链接库形式的接口,这样可以方便地根据需要修改脚本代码,而不用重新编译链接二进 ...
- Isight Linux 2016hf2 安装步骤
把License文件整个拷进去,都给执行权限 把ABAQUS.lic 里的 this_host 改为psn004 27011 改为26011 (区别于2017hf2) 杀掉2017的server ./ ...
- 图解Go select语句原理
Go 的select语句是一种仅能用于channl发送和接收消息的专用语句,此语句运行期间是阻塞的:当select中没有case语句的时候,会阻塞当前的groutine.所以,有人也会说select是 ...
- 关于学习python的想法
选择学习python,就是一次对自己的挑战.自己之前并没有python的基础,只是学习了一点C语言的知识.对于这个课程了解的不是很多,只是上学期在网上自学了一点,自己也是对这门语言感兴趣,所以这个学期 ...
- Unity Button事件的简洁处理
看到很多人依然还是通过最原始的方法给button绑定事件并处理,这种通过Find往子集一个个的查找,获取到后再绑定事件这种操作很费事,有些人则是对查找对象写了个方法自动往子集遍历更方便获取对象,但还是 ...