如何从Observable.from中收集发射值的数组?

时间:2017-02-18 06:00:39

标签: angular asynchronous rxjs reactive-programming observable

所以在Rxjs中,我有很多代码,

return Observable.from(input_array)
           .concatMap((item)=>{
               //this part emits an Observable.of<string> for each item in the input_array
           })
           .scan((output_array:string[],each_item_output_array:string)=>{
               return output_array.push(each_item_output_array) ;
           });

但显然这是错误的,扫描会破坏concatMap中的代码,所以我想知道如何收集可观察的from运算符中每个项的输出数组?

3 个答案:

答案 0 :(得分:25)

在致电scan时,您尚未为累加器指定种子。在那种情况下,第一个值用作种子。例如:

&#13;
&#13;
Rx.Observable
  .from(["a", "b", "c"])
  .scan((acc, value) => acc + value)
  .subscribe(value => console.log(value));
&#13;
<script src="https://unpkg.com/rxjs@5/bundles/Rx.min.js"></script>
&#13;
&#13;
&#13;

在您的代码段中,第一个值不是数组,因此您无法在其上调用push。要将值累积到数组中,可以指定如下的数组种子:

&#13;
&#13;
Rx.Observable
  .from(["a", "b", "c"])
  .concatMap(value => Rx.Observable.of(value))
  .scan((acc, value) => {
    acc.push(value);
    return acc;
  }, []) // Note that an empty array is use as the seed
  .subscribe(value => console.log(JSON.stringify(value)));
&#13;
<script src="https://unpkg.com/rxjs@5/bundles/Rx.min.js"></script>
&#13;
&#13;
&#13;

虽然,对于某些用例,最好不要改变数组:

&#13;
&#13;
Rx.Observable
  .from(["a", "b", "c"])
  .concatMap(value => Rx.Observable.of(value))
  .scan((acc, value) => [...acc, value], [])
  .subscribe(value => console.log(JSON.stringify(value)));
&#13;
<script src="https://unpkg.com/rxjs@5/bundles/Rx.min.js"></script>
&#13;
&#13;
&#13;

请注意scan为它接收的每个值发出一个数组。如果您只想在observable完成时发出一个数组,则可以使用toArray运算符:

&#13;
&#13;
Rx.Observable
  .from(["a", "b", "c"])
  .concatMap(value => Rx.Observable.of(value))
  .toArray()
  .subscribe(value => console.log(JSON.stringify(value)));
&#13;
<script src="https://unpkg.com/rxjs@5/bundles/Rx.min.js"></script>
&#13;
&#13;
&#13;

答案 1 :(得分:0)

另一个选项是bufferCount(count),如果您知道输入数组的长度,则可以得到包含该项目数的单个输出。与不必记住如何使用scan相比,语法更简洁。

注意:如果您不知道大小(尽管在示例中您会知道),则count表示最大值-但这可能会限制资源,因此不要仅将其设置为99999。

 const array = source$.pipe(bufferCount(10));

在我的情况下,我有一个正在执行的“操作”列表,我事先知道步骤总数,因此bufferCount工作得很好。但是请务必仔细考虑错误情况

 // one approach to handling errors that still returns an output array
 // only use this if a single failure shouldn't stop the collection
 const array = source$.pipe(
                    map((result) => ({ hasError: false, result: result }),
                    catchError((err) => ({ hasError: true, error: err }),
                    bufferCount(10));

根据可观察对象的实际情况,如何处理错误的决定将有很大不同-但这里的要点是显示bufferCount()作为选项。

(还有其他buffer个操作可用)

答案 2 :(得分:0)

请谨慎使用此代码:

      const obs = Rx.Observable
      .from(["a", "b", "c"])
      .concatMap(value => Rx.Observable.of(value))
      .scan((acc, value) => {
        acc.push(value);
        return acc;
      }, []); 
      obs.subscribe(value => console.log(JSON.stringify(value)));
      obs.subscribe(value => console.log(JSON.stringify(value)));

结果会有点出乎意料:

 ["a"]
 ["a","b"]
 ["a","b","c"]
 ["a","b","c","a"]
 ["a","b","c","a","b"]
 ["a","b","c","a","b","c"]

“ acc”变量是参考对象,每个订阅者获取流数据并将数据再次添加到同一对象。 避免这种情况可能有很多解决方案,这是在再次接收流数据时创建新对象:

    var obs = Rx.Observable
          .from(["a", "b", "c"])
          .concatMap(value => Rx.Observable.of(value))
          .scan((acc, value) => {
          //clone initial value
            if (acc.length == 0) {

                   acc = [];
                }
            acc.push(value);
            return acc 
          }, []); // Note that an empty array is use as the seed
          obs.subscribe(value => console.log(JSON.stringify(value)));
          obs.subscribe(value => console.log(JSON.stringify(value)));

预期结果:

 ["a"]
 ["a","b"]
 ["a","b","c"]
 ["a"]
 ["a","b"]
 ["a","b","c"]

我希望它可以为某人节省很多时间