2015-12-02 13 views
7

Ho tre promesse, richieste di rest di ritorno liste di dati.
Il terzo ha riferimenti (id) ai primi due elenchi, quindi desidero associare questi ID ai nomi corrispondenti quando ho tutti i dati.
La mappatura non è un problema, io uso solo Lodash per quello.
Ma il problema è aspettare che le tre promesse si risolvano prima di iniziare a calcolare questa mappatura.RxJS: concat tre promesse, distinguere i risultati

ho capito di usare concat():

Rx.Observable.concat(p1, p2, p3).subscribe(
function onNext(list) 
{ 
    // Assign the list to the corresponding variable in the scope 
}, 
function onError(e) 
{ 
    // Notify of error 
}, 
function onCompleted() 
{ 
    // Do the mapping 
} 
); 

mio problema è che onNext() saranno chiamati in ordine casuale. Vale a dire. Non so quale lista ricevo ad un certo punto, ed è difficile capire dai dati che contiene.

C'è un modo per tenere traccia delle promesse prodotte quale elenco? Una specie di zip? concatMap? concatMapObserver? Ammetto di non aver compreso appieno gli ultimi due ...

risposta

6

Se queste sono le promesse di cui stiamo parlando, penso che si possa dare un'occhiata all'operatore forkJoin. Cf. https://github.com/Reactive-Extensions/RxJS/blob/master/doc/api/core/operators/forkjoin.md, http://xgrommx.github.io/rx-book/content/observable/observable_methods/forkjoin.html

Si potrebbe quindi avere qualcosa di simile:

Rx.Observable.forkJoin(p1, p2, p3, function(p1,p2,p3){ 
/* your combining code here */ 
}) 

Insomma, forkJoin ha semantica simile a quella di RSVP.all se si conoscono le promesse RSVP biblioteca.

+0

Grazie. Ho scoperto come concat funziona davvero (vedi la mia risposta), ma preferisco forkJoin, che corrisponde a ciò che volevo. Ho completamente trascurato questo ... :-) – PhiLho

+1

Se qualcuno sta provando a farlo funzionare in Angular, non lo farà, poiché l'evento "completo" non viene attivato dagli osservabili http. Per una soluzione simile, ha funzionato per me usando invece "combineLatest". – frandevel

4

ho fatto alcuni esperimenti di questa mattina, prima di andare a vedere se ho una risposta ... :-)

Potete vederli http://plnkr.co/edit/3Xczzw

ho scoperto che concat di osservabili verrà eseguito ogni osservabile a sua volta, nell'ordine indicato.

var o1 = Rx.Observable.interval(1500).take(1).map(function (i) { return { n: i, id: 'First', ts: Date.now() - reference }}); 
    var o2 = Rx.Observable.interval(1000).take(2).map(function (i) { return { n: i, id: 'Second', ts: Date.now() - reference }}); 
    var o3 = Rx.Observable.interval(2000).take(1).map(function (i) { return { n: i, id: 'Third', ts: Date.now() - reference }}); 
    // Alternative 
    var oa = Rx.Observable.timer(1200).map(function (i) { return { n: i, id: 'Alternative', ts: Date.now() - reference }}); 

    Rx.Observable.concat(o1, o2, o3, oa).subscribe(
     function onNext(v) { v.timestamp = Date.now() - reference; showObject(v); }, 
     function onError(e) { var ts = Date.now() - reference; showHTML("Error " + JSON.stringify(e) + " at " + ts); }, 
     function onCompleted() { var ts = Date.now() - reference; showHTML("Completed at " + ts); } 
); 

{"n":0,"id":"First","ts":1503,"timestamp":1503} 
{"n":0,"id":"Second","ts":2504,"timestamp":2504} 
{"n":1,"id":"Second","ts":3505,"timestamp":3505} 
{"n":0,"id":"Third","ts":5506,"timestamp":5506} 
{"n":0,"id":"Alternative","ts":6708,"timestamp":6708} 
Completed at 6708 

Un concat di promesse non consegnerà nulla prima della prima promessa si risolve. Quindi può consegnare (cioè chiamare su Avanti) le altre promesse risolte, sempre nell'ordine indicato. Poi vedo l'ora per la prossima promessa se eventuali resti, ecc

var p1 = promiseInTime(1500, { id: 'First'}); 
    var p2 = promiseInTime(1000, { id: 'Second' }); 
    var p3 = promiseInTime(2000, { id: 'Third' }); 
    var pa = promiseInTime(1200, { id: 'Failed? ' + !!withFailure }, withFailure); 

    Rx.Observable.concat(p1, p2, p3, pa).subscribe(
     function onNext(v) { v.timestamp = Date.now() - reference; showObject(v); }, 
     function onError(e) { var ts = Date.now() - reference; showHTML("Error " + JSON.stringify(e) + " at " + ts); }, 
     function onCompleted() { var ts = Date.now() - reference; showHTML("Completed at " + ts); } 
); 

{"id":"First","promiseTimeout":1500,"timestamp":1501} 
{"id":"Second","promiseTimeout":1000,"timestamp":1506} 
{"id":"Third","promiseTimeout":2000,"timestamp":2001} 
Error {"id":"Failed? true","promiseTimeout":1201} at 2004 

o

{"id":"First","promiseTimeout":1500,"timestamp":1501} 
{"id":"Second","promiseTimeout":1000,"timestamp":1503} 
{"id":"Third","promiseTimeout":2000,"timestamp":2000} 
{"id":"Failed? false","promiseTimeout":1201,"timestamp":2004} 
Completed at 2004 

Quindi, in sostanza, concat rispetta l'ordine degli argomenti, che può essere un modo per ritrovare quale richiesta ha emesso un risultato di promessa.
Nel caso di richieste Ajax, è meglio concatenare le promesse di quelle osservabili, in quanto saranno richieste in parallelo, non in sequenza (a meno che non sia necessario il secondo, ovviamente).

Ho provato la soluzione fornita da @ user3743222, ed è una buona soluzione. Preferisco forkJoin, poiché i risultati sono assegnati esplicitamente ai parametri, invece di fare affidamento sull'ordine.

Devi essere consapevole delle differenze nella gestione degli errori: ho scoperto che concat elaborerà tutte le promesse, fino a quando non viene rilevato il primo errore. Mentre forkJoin non elaborerà nulla se uno degli errori promette. Il che ha senso (non possiamo unirci a risultati parziali, in generale).

+0

Bel esperimento. Vorrei anche aggiungere che eseguendo le cose in modo sequenziale, i risultati si ottengono più lentamente rispetto a quando li si esegue in parallelo. – user3743222