简体   繁体   English

Angular和rxJS。 流永远不会完成

[英]Angular and rxJS. Stream never complete

I have a menu in angular app and this menu controls the selected client in my web application. 我在angular应用程序中有一个菜单,此菜单控制Web应用程序中的选定客户端。

As it is : 由于它是:

  • My menu is a component which shares the selected client through a service. 我的菜单是通过服务共享所选客户端的组件。
  • The selected client is shared as an Observable. 所选客户端作为可观察对象共享。
  • The observable never complete. 可观察的永远不会完成。 It just emit the new selected client when the menu is clicked. 单击菜单时,它将仅发出新选择的客户端。
  • This is a replay subject. 这是重播主题。 Each new subscriber retrieve the last emitted client. 每个新订户检索最后发出的客户端。

it seemed like a good design but I encountered some problem when making new observables based on the client one (never completing observables). 这似乎是一个不错的设计,但是在基于客户端的新可观察对象(从未完成可观察对象)时遇到了一些问题。 AFAIK this is due that the first observable never completesand this property will propagate. AFAIK这是由于第一个可观察到的对象永远不会完成,并且此属性将传播。

//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}];
}

I'd like to know if more experienced developers think a never ending observable is a good design and what are the alternatives to avoid never ending observables. 我想知道是否更有经验的开发人员认为永无止境的可观察性是一个好的设计,以及避免永无止境的可观察性的替代方法是什么?

you can use unsubscribe function to stop this: for example: mySubscribtion: Subscription; 您可以使用取消订阅功能停止此操作:例如:mySubscribtion:Subscription;

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

and then you can unsubscibe on event or onDestroy that way: 然后您可以通过以下方式取消订阅事件或onDestroy:

this.mySubscribtion.unsubscribe();

As I mentioned above, think about observables as laying a pipeline. 正如我上面提到的,将可观察对象视为管道。 The only time there's a problem is when the water keeps coming (as with Observable.interval(1000) source - it's going to keep ticking). 唯一有问题的是何时水不断涌入(与Observable.interval(1000)源一样,它将不断滴答作响)。 In that situation, if you manually subscribe to the observable, you also need to unsubscribe. 在这种情况下, 如果您手动订阅可观察对象,则还需要退订。

But, as you said, you are using the async pipe and Angular takes care of the unsubscribing there. 但是,正如您所说的,您正在使用异步管道,Angular负责那里的取消订阅。

With you menu clicks, one click will send one value. 单击菜单后,单击一次将发送一个值。 It's quite common for an observable never to complete (ie never receive the completed event), it's not a requirement. 可观察到的永不完成(即永不接收已完成的事件)是很常见的,这不是必需的。

The completed event is generally useful for aggregating operators such as toArray() so that they know the whole set of values. 通常,complete事件对于聚合操作符(例如toArray()很有用,这样它们就可以知道整个值集。

I would suggest just using 我建议只使用

const commandsObs = this.clientService.getClient();

and in the template (example) 并在模板中(示例)

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

Forget about the flatMap, unless it's intending to do something fancy - let me know. 除非打算做一些花哨的事情,否则请别忘了flatMap-让我知道。

Edit - suggested changes to new sample code 编辑-对新示例代码的建议更改

You can try moving the reduce and map inside the flatmap, as they are intended to process the results of the inner observable (Observable.merge). 您可以尝试在平面图中移动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) )
  });

An alternative version to try, 可以尝试的替代版本,

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

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM