在扫描运算符上为每个订阅创建一个新的种子对象

时间:2017-11-11 13:13:56

标签: javascript typescript rxjs rxjs5

RxJS 5.5.2

我正在使用以下代码将一组数字拆分为一个对象,其中包含2个属性'small',用于小于4的数字和'big'其余部分。

const o = from([1, 2, 3, 4, 5, 6]).pipe(
  scan<number, {}>((a, b) => {
    if (b < 4) {
      a['small'].push(b);
    } else {
      a['big'].push(b);
    }
    return a;
  }, {
    'small': [],
    'big': []
  })
);
console.log('subscription 1');
o.subscribe(x => console.log(JSON.stringify(x)));
console.log('subscription 2');
o.subscribe(x => console.log(JSON.stringify(x)));

订阅1台控制台打印后:

{"small":[1,2,3],"big":[4,5,6]} // this is ok

订阅后2台控制台打印:

{"small":[1,2,3,1,2,3],"big":[4,5,6,4,5,6]} // this is not ok

每次有人订阅时,有没有办法以新的种子对象开始?

2 个答案:

答案 0 :(得分:2)

扫描累加器({ small: [], big: [] })使用.push进行变异,这是一种反模式,很容易导致意外行为。

防止更改先前发出的值的一个选项可能是:

scan<number, {}>((a, b) => {
  if (b < 4) {
    return Object.assign({}, a, {small: a.small.concat([b])});
  } else {
    return Object.assign({}, a, {big: a.big.concat([b])}); 
  }
}, {
  'small': [],
  'big': []
})

不确定您要完成的是什么,但是查看partition运算符可能是值得的,它会产生两个独立的值流,如const [small, big] = someStream.partition(x => x < 4);

答案 1 :(得分:2)

另一个选择是将管道包装在defer块中,该块将在订阅时重建源流。

defer(() =>
  from([1, 2, 3, 4, 5, 6]).pipe(
    scan<number, {}>((a, b) => {
      if (b < 4) {
        a['small'].push(b);
      } else {
        a['big'].push(b);
      }
      return a;
    }, {
      'small': [],
      'big': []
    })
  )
);

每个订阅都会在defer块中调用该方法并订阅结果。虽然正如@arturgrzesiak所提到的,变异数组在函数式编程和扩展函数式反应式编程中被视为反模式。