J'apprends RxJava et, comme ma première expérience, j'essaie de réécrire le code dans la première méthode run()
dans ce code (cité sur le blog de Netflix comme problème RxJava peut aider à résoudre) pour améliorer son asynchronicité en utilisant RxJava, c'est-à-dire qu'il n'attend pas le résultat du premier Futur (f1.get()
) avant de passer au reste du code.
f3
dépend de f1
. Je vois comment gérer cela, flatMap
semble faire l'affaire:
Observable<String> f3Observable = Observable.from(executor.submit(new CallToRemoteServiceA()))
.flatMap(new Func1<String, Observable<String>>() {
@Override
public Observable<String> call(String s) {
return Observable.from(executor.submit(new CallToRemoteServiceC(s)));
}
});
Suivant, f4
et f5
dépend de f2
. J'ai ceci:
final Observable<Integer> f4And5Observable = Observable.from(executor.submit(new CallToRemoteServiceB()))
.flatMap(new Func1<Integer, Observable<Integer>>() {
@Override
public Observable<Integer> call(Integer i) {
Observable<Integer> f4Observable = Observable.from(executor.submit(new CallToRemoteServiceD(i)));
Observable<Integer> f5Observable = Observable.from(executor.submit(new CallToRemoteServiceE(i)));
return Observable.merge(f4Observable, f5Observable);
}
});
Ce qui commence à devenir bizarre (merge
en faire n'est probablement pas ce que je veux ...) mais me permet de le faire à la fin, pas tout à fait ce que je veux:
f3Observable.subscribe(new Action1<String>() {
@Override
public void call(String s) {
System.out.println("Observed from f3: " + s);
f4And5Observable.subscribe(new Action1<Integer>() {
@Override
public void call(Integer i) {
System.out.println("Observed from f4 and f5: " + i);
}
});
}
});
Cela me donne:
Observed from f3: responseB_responseA
Observed from f4 and f5: 140
Observed from f4 and f5: 5100
qui est tous les nombres, mais malheureusement j'obtiens les résultats dans des appels séparés, donc je ne peux pas tout à fait remplacer le println final dans le code original:
System.out.println(f3.get() + " => " + (f4.get() * f5.get()));
Je ne comprends pas comment accéder à ces deux valeurs de retour sur la même ligne. Je pense qu'il y a probablement une programmation fonctionnelle qui me manque ici. Comment puis-je faire ceci? Merci.
Il semble que tout ce dont vous avez vraiment besoin est un peu plus d'encouragement et de perspective sur la façon dont RX est utilisé. Je vous suggère de lire plus dans la documentation ainsi que les diagrammes en marbre (je sais qu'ils ne sont pas toujours utiles). Je suggère également d'examiner la fonction lift()
et les opérateurs.
map
, flatMap
et filter
est de manipuler les données dans votre flux de donnéesLe but des opérateurs est de vous permettre de perturber un flux constant d'observables et de définir vos propres opérations sur un flux de données. Par exemple, j'ai codé un opérateur à moyenne mobile. Cela résume n
double
s dans un Observable de doubles pour renvoyer un flux de moyennes mobiles. Le code ressemblait littéralement à ceci
Observable movingAverage = Observable.from(mDoublesArray).lift(new MovingAverageOperator(frameSize))
Vous serez soulagé de constater que de nombreuses méthodes de filtrage que vous considérez comme acquises ont toutes lift()
sous le capot.
Cela étant dit; tout ce qu'il faut pour fusionner plusieurs dépendances est:
map
ou flatMap
Modifier: quelqu'un a converti le texte suivant, que j'avais ajouté en tant que modification de la question, en une réponse, que j'apprécie et que je comprends peut-être bon SO chose à faire, cependant Je ne considère pas cela comme une réponse car ce n'est clairement pas la bonne façon de le faire. Je n'utiliserais jamais ce code et je ne conseillerais à personne de le copier. Autres/meilleures solutions et commentaires bienvenus!
J'ai pu résoudre ce problème avec les éléments suivants. Je ne savais pas que vous pouviez flatMap
un observable plus d'une fois, j'ai supposé que les résultats ne pouvaient être consommés qu'une seule fois. J'ai donc juste flatMap
f2Observable deux fois (désolé, j'ai renommé certaines choses dans le code depuis ma publication d'origine), puis Zip
sur tous les observables, puis abonnez-vous. Ce Map
dans le Zip
pour agréger les valeurs n'est pas souhaitable en raison du type de jonglage. Autres/meilleures solutions et commentaires bienvenus! Le le code complet est visible dans un Gist . Je vous remercie.
Future<Integer> f2 = executor.submit(new CallToRemoteServiceB());
Observable<Integer> f2Observable = Observable.from(f2);
Observable<Integer> f4Observable = f2Observable
.flatMap(new Func1<Integer, Observable<Integer>>() {
@Override
public Observable<Integer> call(Integer integer) {
System.out.println("Observed from f2: " + integer);
Future<Integer> f4 = executor.submit(new CallToRemoteServiceD(integer));
return Observable.from(f4);
}
});
Observable<Integer> f5Observable = f2Observable
.flatMap(new Func1<Integer, Observable<Integer>>() {
@Override
public Observable<Integer> call(Integer integer) {
System.out.println("Observed from f2: " + integer);
Future<Integer> f5 = executor.submit(new CallToRemoteServiceE(integer));
return Observable.from(f5);
}
});
Observable.Zip(f3Observable, f4Observable, f5Observable, new Func3<String, Integer, Integer, Map<String, String>>() {
@Override
public Map<String, String> call(String s, Integer integer, Integer integer2) {
Map<String, String> map = new HashMap<String, String>();
map.put("f3", s);
map.put("f4", String.valueOf(integer));
map.put("f5", String.valueOf(integer2));
return map;
}
}).subscribe(new Action1<Map<String, String>>() {
@Override
public void call(Map<String, String> map) {
System.out.println(map.get("f3") + " => " + (Integer.valueOf(map.get("f4")) * Integer.valueOf(map.get("f5"))));
}
});
Et cela me donne la sortie souhaitée:
responseB_responseA => 714000
Je pense que ce que vous recherchez est switchmap. Nous avons rencontré un problème similaire où nous avons un service de session qui gère l'obtention d'une nouvelle session à partir d'une API, et nous avons besoin de cette session avant de pouvoir obtenir plus de données. Nous pouvons ajouter à la session observable qui renvoie le sessionToken pour une utilisation dans notre appel de données.
getSession renvoie un observable;
public getSession(): Observable<any>{
if (this.sessionToken)
return Observable.of(this.sessionToken);
else if(this.sessionObservable)
return this.sessionObservable;
else {
// simulate http call
this.sessionObservable = Observable.of(this.sessonTokenResponse)
.map(res => {
this.sessionObservable = null;
return res.headers["X-Session-Token"];
})
.delay(500)
.share();
return this.sessionObservable;
}
}
et getData prend cela observable et y ajoute.
public getData() {
if (this.dataObservable)
return this.dataObservable;
else {
this.dataObservable = this.sessionService.getSession()
.switchMap((sessionToken:string, index:number) =>{
//simulate data http call that needed sessionToken
return Observable.of(this.dataResponse)
.map(res => {
this.dataObservable = null;
return res.body;
})
.delay(1200)
})
.map ( data => {
return data;
})
.catch(err => {
console.log("err in data service", err);
// return err;
})
.share();
return this.dataObservable;
}
}
Vous aurez toujours besoin d'un flatmap pour combiner les observables non dépendants.
Plunkr: http://plnkr.co/edit/hiA1jP?p=info
Où j'ai eu l'idée d'utiliser la carte de commutation: http://blog.oughttram.io/angular/2016/01/06/taking-advantage-of-observables-in-angular2.html