如何取消订阅或处置Angular2或RxJS条件下可观察到的区间?

时间:2017-02-03 15:36:17

标签: javascript angular asynchronous rxjs observable

我是整个Rx事物和反应式编程的新手,但是我必须处理这种情况。我想要一个可观察的间隔来检查硬件的状态,每隔500ms通过POST请求检查一次硬件的状态,以查看响应是否发生变化。所以一旦它发生变化,我希望这种间隔可观察的POST请求立即关闭,将资源留给其他未来的操作。这是一段代码。

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() ;
                            }
                        ) ;
                    })

            })
    }
}

2 个答案:

答案 0 :(得分:3)

因此,您希望每500毫秒发出一次POST请求,然后检查其响应。我假设你的方法intervalCheckingStatus评估POST响应并确定它是否不同?

首先,我不会使用IntervalObservable。 你导入了RxJS模块吗?这是Angular认可的第三方库,以及他们在所有开发人员指南样本中使用的库。如果没有,请安装并导入。 https://github.com/Reactive-Extensions/RxJS

import * as Rx from 'rxjs/Rx';

我假设你已经导入了Http,ResponseOptions等,但是这里是以防其他人好奇的:

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

编辑1:忘记包含依赖注入。将Http注入构造函数。我称它为http,因此我如何调用this.http.post

constructor(private http: Http) {

然后,我会做以下事情:

编辑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);

如果您需要在响应具有您正在寻找的状态(或任何属性)时执行某些操作,请将.subscribe链接到结尾并执行您需要的操作。同样,由于take(1),只要第一个元素被抽出,可观察流就完成了,你不需要取消订阅。

此外,这是一个非常有用的网站: http://rxmarbles.com/#take 您可以看到,在他们的示例中,在获取2个元素之后,生成的observable是完整的(垂直线)。

答案 1 :(得分:2)

您可以使用Observable.fromconcatMap来遍历所有项目,然后使用filtertake(1)结合使用,以便在验证通过后立即停止间隔filter

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")
    );

这是一个带有模拟数据

的实例

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>