Angular和rxJS。流永远不会完成

时间:2017-10-30 10:10:26

标签: angular rxjs observable

我在角应用程序中有一个菜单,此菜单控制我的Web应用程序中的所选客户端。

原样:

  • 我的菜单是通过服务共享所选客户端的组件。
  • 选定的客户端作为Observable共享。
  • 观察者永远不会完成。单击菜单时,它只会发出新选择的客户端。
  • 这是一个重播主题。每个新订户都会检索最后一个发出的客户端。

它似乎是一个很好的设计但是在基于客户端(从未完成observables)创建新的observable时遇到了一些问题。 AFAIK这是因为第一个observable永远不会完成,这个属性将传播。

//Command
export interface Command {
    productName: string;
    qty: number;
}

//The service
@Injectable()
export class ClientService {
private _client: ReplaySubject<Client> = new ReplaySubject(1);

    setClient(client: Client) { // The menu component calls setClient on a user click
        this._client.next(client);
    }
    getClient(): Observable<Client> { // getClient is heavilly called in child component to observe client selection events.
        return this._client.asObservable();
    }
}


getCommands(): Observable<Command> { //Used in a template with async pipe

    //In a child component
    const commandsObs = this.clientService.getClient()
    //.take(1) //I am forced to use take(1) to get getCommands() observer to complete
    .flatMap(c => {
        return Observable.merge(getCommandsPromise1(c), getCommandsPromise2(c));
    })
    .reduce((acc, next) => Object.assign({}, acc, next))
    .map(next => {
        return finishMakingCommand(next));
    })
    .catch(err => /* Some error management code*/)
}

getCommandsPromise1(client: Client): Promise<any> {
    //REST call returning a promise
    return Promise.resolve({ 'command1': { 'productName': 'toy', qty: 1 } });
}
getCommandsPromise2(client: Client): Promise<any> {
    //REST call returning a promise
    return Promise.resolve({ 'command2': { 'productName': 'another toy', qty: 1 } });
}

finishMakingCommand(commands: any): Command[] {
    // Flattens 'command1' and 'command2' to a list of commands
    return [{'productName': 'toy', qty: 1}, {'productName': 'another toy', qty: 2}];
}

我想知道更有经验的开发人员是否认为永无止境的可观察性是一个好的设计,以及避免永无止境的可观察性的替代方案。

2 个答案:

答案 0 :(得分:0)

您可以使用取消订阅功能来阻止此操作:  例如:      mySubscribtion:订阅;

 this.mySubscription = somthing.subscribe((...) => {...})

然后您可以通过这种方式取消对事件或onDestroy的取消:

this.mySubscribtion.unsubscribe();

答案 1 :(得分:0)

正如我上面提到的,将可观察量视为铺设管道。唯一出现问题的时候是水继续流动(就像Observable.interval(1000)来源一样 - 它会继续滴答作响)。在这种情况下, 如果 您手动订阅了observable,您还需要取消订阅。

但是,正如你所说,你正在使用异步管道,而Angular会负责取消订阅。

点击菜单后,只需点击一下即可发送一个值。对于一个永远不会完成(即永远不会收到完成的事件)的观察者来说,这是很常见的,这不是必需的。

完成的事件通常可用于聚合toArray()等运算符,以便他们知道整个值集。

我建议只使用

const commandsObs = this.clientService.getClient();

并在模板中(示例)

<div>{{ commandObs | async }}</div>

忘记flatMap,除非它打算做一些奇特的事 - 让我知道。

修改 - 建议更改新的示例代码

您可以尝试在flatmap中移动reduce和map,因为它们用于处理内部observable(Observable.merge)的结果。

const commandsObs = this.clientService.getClient()
  .flatMap(c => {
    return Observable.merge(getCommandsPromise1(c), getCommandsPromise2(c))
      .reduce((acc, next) => Object.assign({}, acc, next))
      .map(next => finishMakingCommand(next) )
  });

尝试替代版本,

const commandsObs = this.clientService.getClient()
  .map(c => Observable.forkJoin(getCommandsPromise1(c), getCommandsPromise2(c) );