I have the following task to perform:
I need to emit 2 observables (obs1 & obs2) process their results and then call another observable (obs3) and process its results and if possible that while processing the results of obs3 have access to the results of obs1 and obs2.
This is my draft code which doesn't do the trick, how can I alter it.
public void executeFind(String session_id, long template_id, GameModelType game_model) {
Observable<RxMessage<byte[]>> userObs = context.getUser(session_id);
Observable<Game> gameObs = context.findGame(template_id, game_model, GameStateType.WAITING);
Observable.zip(userObs, gameObs, new Func2<RxMessage<byte[]>, Game, GameObject>() {
@Override
public GameObject call(RxMessage<byte[]> userRawReply, ActiveGame game) {
..
..
return context.updateGame(game.getGameData())
.subscribe(new Action1<GameObject>() {
@Override
public void call(GameObject updateReply) {
..
..
}
});
return userReply;
}
});
}
This doesn't really work - I can write a code which uses explicit calls to .flatMap\subscribe for each Observable but results in many nested calls which is obviously poor usage of the framework.
What is the right way to solve this??
Thank you!
EDIT:
I've found this solution to work, but I'm still wondering whether there is a "cleaner" way to achieve this:
public void executeFind(ReplyMessage<JsonObject> replyObj, String session_id, long template_id, GameModelType game_model) throws CommandException {
rx.Observable<GameObject> userObs = context.getUser(session_id);
rx.Observable<Game> gameObs = context.findGame(template_id, game_model, GameStateType.WAITING);
rx.Observable.zip(userObs, gameObs, new Func2<GameObject, Game, List<Object>>() {
@Override
public List<Object> call(GameObject userReply, Game game) {
User user = ...;
final List<Object> results = new ArrayList<Object>(3);
results.add(ErrorCodes.STATUS_OK);
results.add(user);
results.add(game);
context.updateGame(game.getGameData()).subscribe(new Action1<GameObject>() {
@Override
public void call(GameObject updateReply) {
...
}
});
return results;
}
}).subscribe(new Action1<List<Object>>() {
@Override
public void call(List<Object> results) {
int status = (int) results.get(0);
User user = (User) results.get(1);
Game game = (Game) results.get(2);
}
});
}
I would code this thing with the following idea in mind. May be map can be replace with flatMap if that's relevant for your use case. Also note I have only used Java 8 lambdas syntax, but for more readability I strongly advises you to have simple and well named methods (and use them with a method reference) for each of these functions/actions as it will raise understandability of the code (That's what we do on mockito, but everyone should do it in their own code base).
public void executeFind(ReplyMessage<JsonObject> reply_obj, String session_id, long template_id, GameModelType game_model) throws CommandException {
Observable<GameObject> userObs = context.getUser(session_id);
Observable<Game> gameObs = context.findGame(template_id, game_model, GameStateType.WAITING);
Observable.zip(userObs, gameObs, (userReply, game) -> {
User user = ...;
return GameOfUser.gameFound(game, user);
}).map(gou -> {
context.updateGame(gou.gameData()).susbcribe(...);
return gou;
}).subscribe(gou -> ...);
}