简体   繁体   English

如何取消订阅或处理Angular2或RxJS中可观察到的间隔?

[英]How to unsubscribe or dispose an interval observable on condition in Angular2 or RxJS?

I'm new to the whole Rx thing and reactive programming, however I have to deal with such a situation. 我是整个Rx和响应式编程的新手,但是我必须处理这种情况。 I want a interval observable to check a hardware's status by a POST request to its REST API every 500ms to see if the response changes. 我希望每隔500毫秒就可以观察到一个间隔,该间隔可以通过对REST API的POST请求来检查硬件的状态,以查看响应是否发生了变化。 So once it changes, I want such interval observable POST request shut down immediately, leaving resource to other future operations. 因此,一旦更改,我希望立即关闭这种可观察间隔的POST请求,从而将资源留给以后的其他操作。 Here is a piece of code. 这是一段代码。

myLoop(item_array:number[], otheroption : string){
    for (let item of item_array){
        //set hardware option, still a request
        this.configHardware(item,otheroption)
        //after config the hardware, command hardware take action
            .flatMap(() => {
                //this return a session id for check status
                this.takeHardwareAction()
                    .flatMap( (result_id) => {
                        //I want every 500ms check if hardware action is done or not
                        let actionCheckSubscription = IntervalObservable.create(500).subscribe(
                            () => {
                                //So my question is, can I unsubscribe the actionCheckSubscription in here on condition change? For example, 
                                if (this.intervalCheckingStatus(result_id))
                                    actionCheckSubscription.unsubscribe() ;
                            }
                        ) ;
                    })

            })
    }
}

So you want to make a POST request every 500 ms and then check its response. 因此,您想每500毫秒发出一个POST请求,然后检查其响应。 I assume your method intervalCheckingStatus evaluates the POST response and determines whether it's different? 我假设您的方法intervalCheckingStatus评估POST响应并确定它是否不同?

First, I wouldn't use IntervalObservable. 首先,我不会使用IntervalObservable。 Have you imported the RxJS module? 您是否导入了RxJS模块? It's the third party library endorsed by Angular and the one they use in all their developer guide samples. 这是Angular认可的第三方库,也是他们在所有开发人员指南样本中使用的库。 If not, install it and import it. 如果没有,请安装并导入。 https://github.com/Reactive-Extensions/RxJS https://github.com/Reactive-Extensions/RxJS

import * as Rx from 'rxjs/Rx';

I assume you've already imported Http, ResponseOptions etc but here it is in case others are curious: 我假设您已经导入了Http,ResponseOptions等,但是在此情况下,以防其他人感到好奇:

import { Http, Response, ResponseOptions } from '@angular/http';

EDIT 1: Forgot to include the dependency injection. 编辑1:忘记包括依赖项注入。 Inject Http into your constructor. 将Http注入您的构造函数。 I've called it http, hence how I'm calling this.http.post 我已经将其称为http,因此如何称呼它。http.post

constructor(private http: Http) {

Then, I would do the following: 然后,我将执行以下操作:

EDIT 2: This would be inside your loop where the post arguments are relevant to the item in your array. 编辑2:这将在您的循环内,其中post参数与您数组中的项目相关。

    // Every 500 ms, make a POST request
    Rx.Observable.interval(500)
                  // Add your POST arguments here
                 .map(_ => this.http.post(yourUrl, yourBody))
                 // This filter here is so that it will only emit when intervalCheckingStatus returns true
                 // You need to get the property you need from the Response: resp
                 // Is it the status code you're interested in? That's what I put as an example here but whatever it is, pass it to your method
                .filter(resp => this.intervalCheckingStatus(resp.status))
                // Take(1) takes only the first emitted value and once it does that, the observable completes. So you do NOT need to unsubscribe explicitly.
                .take(1);

If you need to do something once the response has the status (or whatever property) you're looking for, then chain a .subscribe to the end and perform the action you need in there. 如果您需要在响应具有所需状态(或任何属性)后执行某项操作,则将.sub链接到末尾并在其中执行所需的操作。 Again, due to take(1), as soon as the first element is pumped, the observable stream completes and you do not need to unsubscribe. 同样,由于take(1),一旦泵入第一个元素,可观察流就完成了,您无需取消订阅。

Also, here is a very helpful website: http://rxmarbles.com/#take You can see that in their example, the resulting observable is complete (vertical line) after 2 elements are taken. 另外,这是一个非常有用的网站: http : //rxmarbles.com/#take您可以看到,在他们的示例中,采用2个元素后,所得的可观察对象是完整的(垂直线)。

You could use Observable.from and concatMap to iterate through all the items and then use a filter in combination with take(1) to stop an interval as soon as the validation passes the filter : 您可以使用Observable.fromconcatMap遍历所有项,然后在验证通过filter filter后结合使用filtertake(1)停止间隔:

myLoop(item_array:number[], otheroption : string) {
    return Observable.from(item_array)
        .concatMap(item => this.configHardware(item, otheroption)
            .switchMap(resultId => Observable.interval(500)
                .switchMapTo(this.intervalCheckingStatus(resultId))
                .filter(status => Boolean(status)) // your logic if the status is valid, currently just a boolean-cast
                .take(1) // and complete after 1 value was valid
                .mapTo(item) // map back to "item" so we can notify the subscriber (this is optional I guess and depends on if you want this feature or not)
            )
        );
}

// usage:
myLoop([1,2,3,4], "fooBar")
    .subscribe(
        item => console.log(`Item ${item} is now valid`),
        error => console.error("Some error occured", error),
        () => console.log("All items are valid now")
    );

Here is a live-example with mock-data 这是一个包含模拟数据的实时示例

 const Observable = Rx.Observable; function myLoop(item_array) { return Observable.from(item_array) // if you don't mind the execution-order you can use "mergeMap" instead of "concatMap" .concatMap(item => configHardwareMock(item) .switchMap(resultId => Observable.interval(500) .do(() => console.info("Checking status of: " + resultId)) .switchMap(() => intervalCheckingStatus(resultId)) .filter(status => Boolean(status)) // your logic if the status is valid, currently just a boolean-cast .take(1) // and complete after 1 value was valid .mapTo(item) // map back to "item" so we can notify the subscriber (this is optional I guess and depends on if you want this feature or not) ) ); } // usage: myLoop([1,2,3,4]) .subscribe( item => console.log(`Item ${item} is now valid`), error => console.error("Some error occured", error), () => console.log("All items are valid now") ); // mock helpers function configHardwareMock(id) { return Observable.of(id); } function intervalCheckingStatus(resultId) { if (Math.random() < .4) { return Observable.of(false); } return Observable.of(true); } 
 <script src="https://unpkg.com/rxjs/bundles/Rx.min.js"></script> 

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

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