将未知数量的可观察量与RxJS合并

时间:2015-08-14 08:58:44

标签: javascript reactive-programming observable rxjs

我目前正在尝试使用RxJS创建一个演示Reactive Programming的小项目。我的目标是向同事们展示这件事情已经存在,值得研究。 我对框架没有经验,所以这会让事情变得复杂。

我正在试图扩展我的另一个演示以利用RxJS。 这不是一个非常复杂的演示,基本上我可以添加任意数量的小形式,它们会产生一个由小公式计算的数字,并且有一个按钮,总结所有形式的值。

在表格内部计算公式很容易,但我想我可以更进一步。

我希望通过合并的observable自动完成sum操作。 我想出的唯一解决方案是:

//dummy observables
var s1 = Rx.Observable.Interval(100);
var s2 = Rx.Observable.Interval(200);

//Combine the observables
var m = s1.combineLatest(s2, function(x,y){return x+y});

//Subscribe to the combined observable
var sub = m.subscribe(function(x){console.log(x)});
//A new observable is created
var s3 = Rx.Observable.Interval(300);
//Now I need to update all my subscriptions, wich is a pain.
m = m.combine(s3, function(x,y){return x+y});
sub.dispose();
sub=m.subscribe(function(x){console.log(x)});

我想我可以得到另一个observable来通知我的订阅者自我更新 - 因为知道我的所有订阅者的工作会使整个架构变得毫无用处,但这对于像这样的任务来说听起来有点过分,而且我不会这样做。只是意味着演示,我真的无法想象每天都会有#34;"现实世界的例子,像这样的架构会让事情变得更干净,而不仅仅是观察任何变化,并且积极地获得计算出来的值"从我的表格。 我可能会对处理表单的模块中的值进行主动获取和求和,并使用" m"可以观察外部世界订阅,从模块内部将我的值推入其中。

这是一种正确的方法吗?我认为是的,因为它们归我的模块所有,我应该完全控制它们发生的事情,但我真的对更有经验的人对此的看法感兴趣。

5 个答案:

答案 0 :(得分:1)

我认为您不会找到直接做您需要的操作员。

虽然制作自己的运营商没有错:

var source = //An observable of observables of form data

Observable.prototype.combineLatestObservable = function(resultSelector) {
  var source = this;
  return Rx.Observable.create(function(obs) {
    var disposable = new Rx.SerialDisposable();
    var sources= [];
    return source.subscribe(
      function(x) {
        //Update the set of observables
        sources.push(x);
        //This will dispose of the previous subscription first
        //then subscribe to the new set.
        disposable.seDisposable(Rx.Observable.combineLatest(sources, resultSelector)
                                             .subscribe(obs));
      }, 
      function(e) { obs.onError(e); }, 
      function() { obs.onCompleted(); });
  }).share();
}

或者如果您想要与运营商合作:

//Have to use arguments since we don't know how many values we will have
function sums() {
  var sum = 0;
  for (var i = 0, len = arguments.length; i < len; ++i) { 
    sum += arguments[i]; 
  }
  return sum;
}

source
//Capture the latest set of Observables
.scan([], function(acc, x) {
  acc.push(x);
  return acc;
})
//Dispose of the previous set and subscribe to the new set
.flatMapLatest(function(arr) {
  return Observable.combineLatest(arr, sums);
})
//Don't know how many subscribers you have but probably want to keep from 
//recreating this stream for each
.share();

答案 1 :(得分:1)

我正在使用此片段合并未知数量的可观察量 -

    someArrayOfIdsToPerformRequest: [];
    this.isLoading = true;
    const mergedRequests: Array<Promise<JSON>> = [];
    this.someArrayOfIdsToPerformRequest.forEach((item) => {
        mergedRequests.push(this.postsService.remove({id: item.id}) //returns observable
            .toPromise());
    });
    Promise.all(mergedRequests)
        .then(() => {
            this.isLoading = false;
            this.loadPosts();
        });

希望这有助于某人!

答案 2 :(得分:0)

如果你看一下combineLatest Observable你可以看到,它需要一个可变数量的// Whatever Observables you want... const a = Rx.Observable.of(1,2,3) const b = Rx.Observable.interval(100).take(5) const c = Rx.Observable.of(2) Rx.Observable.combineLatest(a, b, c) // notice, I didn't define a resultSelector .do(numbers => console.log(numbers)) // you get 3 element arrays .map(numbers => numbers.reduce((sum, n) => sum + n, 0)) .subscribe(sum => console.log(sum)) ,而不仅仅是一个[3, 0, 2] // array printed in the do block 5 // the sum of the array printed from the subscription [3, 1, 2] 6 [3, 2, 2] 7 [3, 3, 2] 8 [3, 4, 2] 9 。考虑到这一点,你可以做这样的事情:

Observable

我的例子的输出将是:

Function#apply

如果您想使用Rx.Observable.combineLatest.apply(Rx.Observable, [a, b, c]) 数组调用此标记,可以使用getSupportActionBar().setDisplayShowTitleEnabled(true); getSupportActionBar().setTitle("Hello"); 执行此操作:

<!-- Base application theme. -->
<style name="AppTheme" parent="Theme.AppCompat.Light.NoActionBar">
    <!-- Customize your theme here. -->
    <item name="colorPrimary">@color/colorPrimary</item>
    <item name="colorPrimaryDark">@color/colorPrimaryDark</item>
    <item name="colorAccent">@color/colorAccent</item>
</style>

希望我没有误解你的要求

答案 3 :(得分:0)

我知道我参加这个聚会迟到了,但是我只是有同样的问题,无法找到合适的答案。我已经弄清楚了如何在我的应用程序上下文中执行此操作,因此,除非这仍然是您的未解决问题,否则我将在此处发布答案以供后代参考。

要设置我的问题,我有一个带有许多控件的Angular表单。每当验证状态在任何一个控件上更改时,我都需要做一些事情。但是,控件可以在以后动态添加到表单中,我仍然需要侦听这些单独控件上的验证更改。

我将尝试稍微改变我的方法以适应问题的背景。基本上,是的,正确的方法是在何时让内部Observable更新其订阅时使用外部Observable信号。这是可以接受的体系结构,坦率地说,这是Reactive Extensions如此强大的原因之一。这是我使用RxJS 6.2.2和TypeScript 3.0.1做到的方式。请注意,在询问或先前回答此问题时,此方法可能不可用。

import { map, mergeMap, delay } from 'rxjs/operators';
import { interval, Subject, Observable } from 'rxjs';

// utility function to create sample observables
const createObs = (time: number, i: string) => interval(time).pipe(map(val => `${i}: ${val}`))

// create static sample observables. 
// these could represent the value changes of known form elements
const obsA = createObs(1000, 'A'); // emit every 1 second
const obsB = createObs(2500, 'B'); // emit every 2.5 seconds

// create a Subject which will represent dynamically adding a new stream of form values to the calculation
const formChanges = new Subject<Observable<string>>();

// this will hold the accumulated results
const results: string[] = [];

// subscribe to the subject
// each time any of the inner observables emits a new value, this will log the latest value of all existing inner observables
formChanges.pipe(
  mergeMap(innerObs => innerObs, (outerValue, innerValue, outerIndex, innerIndex) => {
    results[outerIndex] = innerValue;
    return results;
  })
).subscribe(console.log);

// emit the known form value change streams
formChanges.next(obsA);
formChanges.next(obsB);

// this will represent adding some other stream of values to the calculation later
formChanges.next(createObs(1750, 'C').pipe(delay(5000))) // after 5 seconds, emit every 1.75 seconds

// Output
// [ 'A: 0' ]
// [ 'A: 1' ]
// [ 'A: 1', 'B: 0' ]
// [ 'A: 2', 'B: 0' ]
// [ 'A: 3', 'B: 0' ]
// [ 'A: 3', 'B: 1' ]
// [ 'A: 4', 'B: 1' ]
// [ 'A: 5', 'B: 1' ]
// [ 'A: 5', 'B: 1', 'C: 0' ]
// [ 'A: 6', 'B: 1', 'C: 0' ]
// [ 'A: 6', 'B: 2', 'C: 0' ]
// [ 'A: 7', 'B: 2', 'C: 0' ]
// [ 'A: 7', 'B: 2', 'C: 1' ]
// [ 'A: 8', 'B: 2', 'C: 1' ]

答案 4 :(得分:-1)

以下是使用TypeScript编写的一种方法:

    $args = array('parent' => $category->term_id);
    $categories = get_categories( $args );
    foreach($categories as $category) { 
        <a href="<?php echo get_category_link( $category->term_id )?>" title="View posts in <?php echo $category->name?>"><?php $category->name;?></span><!--these are the sub category-->
    }