javascript - RxJS 自定义操作符内部变量

标签 javascript functional-programming rxjs reactivex

在 RxJS 中使用/改变来自自定义运算符闭包的变量有缺点吗?我意识到它违反了“纯”功能原则,您可以使用 scan 作为这个简单的示例,但我特别询问以下基础模式的具体技术问题:

const custom = () => {

  let state = 0; 

  return pipe(
    map(next => state * next),
    tap(_ => state += 1),
    share()
  )
}

// Usage
const obs = interval(1000).pipe(custom())

obs.subscribe()

最佳答案

您在自定义 运算符中存储状态的方式至少存在两个问题。

第一个问题是您这样做意味着运算符不再是引用透明的。也就是说,如果将运算符的调用替换为运算符的返回值,则行为不同:

const { pipe, range } = rxjs;
const { map, share, tap } = rxjs.operators;

const custom = () => {
  let state = 0; 
  return pipe(
    map(next => state * next),
    tap(_ => state += 1),
    share()
  );
};

const op = custom();
console.log("first use:");
range(1, 2).pipe(op).subscribe(n => console.log(n));
console.log("second use:");
range(1, 2).pipe(op).subscribe(n => console.log(n));
.as-console-wrapper { max-height: 100% !important; top: 0; }
<script src="https://unpkg.com/rxjs@6/bundles/rxjs.umd.min.js"></script>

第二个问题 - 正如另一个答案中提到的 - 不同的订阅将在它们的 next 通知中收到不同的值,因为运算符中的状态是共享的。

例如,如果源 observable 是同步的,连续的订阅将看到不同的值:

const { pipe, range } = rxjs;
const { map, share, tap } = rxjs.operators;

const custom = () => {
  let state = 0; 
  return pipe(
    map(next => state * next),
    tap(_ => state += 1),
    share()
  );
};

const source = range(1, 2).pipe(custom());
console.log("first subscription:");
source.subscribe(n  => console.log(n));
console.log("second subscription:");
source.subscribe(n => console.log(n));
.as-console-wrapper { max-height: 100% !important; top: 0; }
<script src="https://unpkg.com/rxjs@6/bundles/rxjs.umd.min.js"></script>

但是,可以编写一个与您的自定义 运算符非常相似的运算符,并让它在所有情况下都正确运行。为此,有必要确保运算符中的任何状态都是每个订阅

管道运算符只是一个接受可观察值并返回可观察值的函数,因此您可以使用 defer 来确保您的状态是按订阅的,如下所示:

const { defer, pipe, range } = rxjs;
const { map, share, tap } = rxjs.operators;

const custom = () => {
  return source => defer(() => {
    let state = 0; 
    return source.pipe(
      map(next => state * next),
      tap(_ => state += 1)
    );
  }).pipe(share());
};

const op = custom();
console.log("first use:");
range(1, 2).pipe(op).subscribe(n => console.log(n));
console.log("second use:");
range(1, 2).pipe(op).subscribe(n => console.log(n));

const source = range(1, 2).pipe(op);
console.log("first subscription:");
source.subscribe(n => console.log(n));
console.log("second subscription:");
source.subscribe(n => console.log(n));
.as-console-wrapper { max-height: 100% !important; top: 0; }
<script src="https://unpkg.com/rxjs@6/bundles/rxjs.umd.min.js"></script>

关于javascript - RxJS 自定义操作符内部变量,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/52020354/

相关文章:

python - 函数式编程方法,或大多数 Pythonic 方法,一次使用两个相邻变量遍历数组

javascript - 为什么我们应该使用 RxJs of() 函数?

angular - 在没有回调参数的情况下调用订阅时,RXJS do 运算符不会在流中被命中

angular - 使用filter()代替ofType()可以吗

javascript - 将 "2011-06-23T13:20:12+0000"转换为时间之前

javascript - 通过单击按钮旋转图像

swift - 使用 ReactiveSwift 添加数字流

javascript - 是否可以在没有内联函数的情况下从 reduce 和 curry 创建 sum 函数?

javascript - 焦点改变时保持文本选择

javascript - js中为什么要用字母作为函数名和参数?