等待所有带有rxjs的observable

时间:2017-10-04 10:04:24

标签: rxjs observable

我有以下代码:

this.hubService.sendScopedCommand(Constants.hangarCommands.getHangarsOfPlayer).then((result: ICommand) => {

    let hangars: IHangar[] = result.arguments[0];

    for (let hangar of hangars) {
        this.pieceService.getGroupedPieces(hangar.pieces).subscribe(group => hangar.groupedPieces = group);
    }

    this.hangars$.next(hangars);
}, (ex: any) => this.hangars$.error(ex));

基本上,sendScopeCommand通过websocket发送内容,并且当在websocket上收到响应时执行then函数。此时,我得到了一个放在hangars的对象数组。

在这些对象中,我有一个玩家拥有的所有棋子的数组。可能有多件具有相同的部件类型,因此我创建了一个功能来对它们进行分组:getGroupedPieces。其代码如下:

public getGroupedPieces(pieces: IPiece[]): Observable<IGroupedPiece[]> {
    return Observable
        .from(pieces)
        .groupBy(p => p.pieceTypeId)
        .flatMap(p => p.toArray())
        .map(p => { return <IGroupedPiece>{ amount: p.length, piece: p[0] }; })
        .toArray();
}

此代码有效,但我确定它不正确。实际上,我认为hangars甚至在for循环中的observable完成之前就会在observable上发出。

我在这里想要的是等待所有这些observable完成,然后在Observable上发出hangars

2 个答案:

答案 0 :(得分:2)

我个人尝试尽可能少地调用subscribe,特别是避免可能的订阅嵌套。

我把一个快速的角度组件拼凑在一起,给出了我将如何处理它的样本。

抱歉,如果它粗糙或拼写错误(破骨领只能单手操作。)

import { Component, OnInit } from '@angular/core';
import { Observable, Subject } from 'rxjs';

interface hangar {
    pieces: number[];
    groupedPieces: number[];
}

@Component({
    selector: 'app-root',
    templateUrl: './app.component.html',
    styleUrls: ['./app.component.css']
})
export class AppComponent {
    title = 'app works!';
    //output mock
    hangars$: Subject<hangar[]> = new Subject<hangar[]>();

    ngOnInit() {
        //proof of functionality
        this.hangars$.subscribe(h => console.log(h));
    }

    //method to mock the then call from example
    start() {

        //mock some data
        let hangars: hangar[] = [{ pieces: [1, 2, 3], groupedPieces: null }, { pieces: [1, 2, 3], groupedPieces: null }, { pieces: [1, 2, 3], groupedPieces: null }];

        //subject to handle observable clean up
        let subManagement$: Subject<any> = new Subject<any>();
        let obsArr: Observable<number[]>[] = [];

        //here were going to build an array the observables but not subscribe to them yet
        hangars.forEach(hangar =>
            obsArr.push(
                this.getGroupedPieces(hangar.pieces)
                    .takeUntil(subManagement$)
                    .do(group => hangar.groupedPieces = group)
            )
        );

        //real magic, this waits for all of the observables responses before emitting its value
        Observable.combineLatest(obsArr).subscribe(
            () => this.hangars$.next(hangars),
            null,
            () => subManagement$.next()//cleanup 
        );
    }

    //mock out your service
    private getGroupedPieces(pcs: number[]): Observable<number[]> {

        return Observable.of([1, 2, 3, 4]).delay(1000);
    }
}

答案 1 :(得分:0)

我认为您可以尝试使用RxJS的forkJoin运算符:

this.hubService.sendScopedCommand(Constants.hangarCommands.getHangarsOfPlayer).then((result: ICommand) => {

    let hangars: IHangar[] = result.arguments[0];
    let hangars$$: Observable<IHangar>[] = hangars.map(hangar => {
      return this.pieceService.getGroupedPieces(hangar.pieces)
    })

    Observable
      .forkJoin(...hangars$$)
      .subscribe(groups => {
        groups.forEach((group, i) => hangars[i].groupedPieces = group)
        this.hangars$.next(hangars);
      })

}, (ex: any) => this.hangars$.error(ex));