在 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/