原文链接: rxjs6 Transformation Operators
上一篇: rxjs6 Combination Operators
下一篇: rxjs6 Filtering Operators
buffer
缓存源流的数据, 在内层发出数据时, 将外层缓存的数据发出
let s1 = interval(1000)
let s2 = interval(300)
s2.pipe(
buffer(s1)
).subscribe(console.log)
// [ 0, 1, 2 ]
// [ 3, 4, 5 ]
// [ 6, 7, 8 ]
bufferCount
第一个参数为发送数据时的buffer长度, 第二个参数是开启新buffer时的buffer长度, 默认为null
let s1 = interval(1000)
let s2 = interval(300)
s2.pipe(
bufferCount(3, 1)
).subscribe(console.log)
bufferTime
在指定时间内缓存数据, 可用于判断双击
let s2 = timer(0, 200)
s2.pipe(
bufferTime(500)
).subscribe(console.log)
// [ 0, 1, 2 ]
// [ 3, 4 ]
// [ 5, 6, 7 ]
// [ 8, 9 ]
bufferToggle
传入开始和结束的流, 缓存期间的数据
//emit value every second
const sourceInterval = interval(1000);
//start first buffer after 5s, and every 5s after
const startInterval = interval(5000);
//emit value after 3s, closing corresponding buffer
const closingInterval = val => {
console.log(`Value ${val} emitted, starting buffer! Closing in 3s!`);
return interval(3000);
};
//every 5s a new buffer will start, collecting emitted values for 3s then emitting buffered values
const bufferToggleInterval = sourceInterval.pipe(
bufferToggle(startInterval, closingInterval)
);
//log to console
//ex. emitted buffers [4,5,6]...[9,10,11]
const subscribe = bufferToggleInterval.subscribe(val =>
console.log('Emitted Buffer:', val)
);
// Value 0 emitted, starting buffer! Closing in 3s!
// Emitted Buffer: [ 4, 5, 6 ]
// Value 1 emitted, starting buffer! Closing in 3s!
// Emitted Buffer: [ 9, 10, 11 ]
// Value 2 emitted, starting buffer! Closing in 3s!
// Emitted Buffer: [ 14, 15, 16 ]
// Value 3 emitted, starting buffer! Closing in 3s!
// Emitted Buffer: [ 19, 20, 21 ]
只会在鼠标抬起时发送鼠标移动的事件数据
let up = fromEvent(document, 'mouseup')
let down = fromEvent(document, 'mousedown')
let move = fromEvent(document, 'mousemove')
move.pipe(
bufferToggle(down, () => up)
).subscribe(console.log);
bufferWhen
在内层流发送数据时将缓存的外层流输出
//emit value every 1 second
const oneSecondInterval = interval(1000);
//return an observable that emits value every 5 seconds
const fiveSecondInterval = () => interval(5000);
//every five seconds, emit buffered values
const bufferWhenExample = oneSecondInterval.pipe(
bufferWhen(fiveSecondInterval)
);
//log values
//ex. output: [0,1,2,3]...[4,5,6,7,8]
const subscribe = bufferWhenExample.subscribe(val =>
console.log('Emitted Buffer: ', val)
);
// Emitted Buffer: [ 0, 1, 2, 3 ]
// Emitted Buffer: [ 4, 5, 6, 7, 8 ]
// Emitted Buffer: [ 9, 10, 11, 12, 13 ]
// Emitted Buffer: [ 14, 15, 16, 17, 18 ]
expand
循环生成数据, 第一个数据会直接发出, 然后以第一个为参数, 一次调用生成函数, 生成新的数据
//emit 2
const source = of(2);
const example = source.pipe(
//recursively call supplied function
expand(val => {
//2,3,4,5,6
console.log(`Passed value: ${val}`);
//3,4,5,6
return of(1 + val);
}),
//call 5 times
take(5)
);
/*
"RESULT: 2"
"Passed value: 2"
"RESULT: 3"
"Passed value: 3"
"RESULT: 4"
"Passed value: 4"
"RESULT: 5"
"Passed value: 5"
"RESULT: 6"
"Passed value: 6"
*/
//output: 2,3,4,5,6
const subscribe = example.subscribe(val => console.log(`RESULT: ${val}`));
groupBy
根据关键词分组
const people = [
{name: 'Anna', score: 100, subject: 'English'},
{name: 'Anna', score: 90, subject: 'Math'},
{name: 'Anna', score: 96, subject: 'Chinese'},
{name: 'Jerry', score: 80, subject: 'English'},
{name: 'Jerry', score: 100, subject: 'Math'},
{name: 'Jerry', score: 90, subject: 'Chinese'},
];
let source = from(people)
let example = source.pipe(
groupBy(person => person.name),
mergeMap(g => {
return g.pipe(
reduce(
(pre, cur) => {
return {
name: pre.name,
score: pre.score + cur.score
}
}, {name: g.key, score: 0}
)
)
}),
)
example.subscribe(console.log);
// { name: 'Anna', score: 286 }
// { name: 'Jerry', score: 270 }
map / mapTo
映射函数, 或映射为固定值
// 1 4 9
of(1, 2, 3).pipe(map(x => x ** 2)).subscribe(console.log)
// abc abc abc
of(1, 2, 3).pipe(map(x => 'abc')).subscribe(console.log)
mergeScan
统计鼠标按下的时间
mouseDown$.pipe(
mapTo(interval(1000).pipe(
takeUntil(mouseUp$),
)),
mergeAll(),
// 不使用scan, 每次按下鼠标时, 时间会清空
scan((pre, cur) => ++pre, 0),
).subscribe(val => (durationElem.innerHTML = `${val}s`));
使用mergeScan
<div id="duration"></div>
<script>
const {fromEventPattern, timer, interval, fromEvent} = rxjs;
const {bufferToggle, mergeAll, mapTo, takeUntil, map, scan, mergeScan} = rxjs.operators;
// reference
const durationElem = document.getElementById('duration');
// streams
const mouseDown$ = fromEvent(document, 'mousedown');
const mouseUp$ = fromEvent(document, 'mouseup');
// accumulate time mouse held down over time
mouseDown$
.pipe(
mergeScan((acc, curr) => {
return interval(1000).pipe(
scan((a, _) => ++a, 0),
map((val) => val + acc),
takeUntil(mouseUp$)
);
}, 0)
// output: 1s...2s...3s...4s...
)
.subscribe(val => (durationElem.innerHTML = `${val}s`));
</script>
pairwise
将流式数据, 转为定长数组
let s = interval(100).pipe(take(10))
s.pipe(
pairwise()
).subscribe(console.log)
// [ 0, 1 ]
// [ 1, 2 ]
// [ 2, 3 ]
// [ 3, 4 ]
partition
根据条件, 将数据分为满足条件的和不满足条件的两组
const observableValues = of(1, 2, 3, 4, 5, 6);
const [evens$, odds$] = partition(observableValues, (value, index) => value % 2 === 0);
// 1 3 5
odds$.subscribe(x => console.log('odds', x));
// 2 4 6
evens$.subscribe(x => console.log('evens', x));
pluck
特定属性的filter, 筛选指定的属性值
of({
name: 'a'
}, {
name: 'b'
}, {
name: 'c'
}).pipe(
pluck('name')
).subscribe(console.log)
// a b c
scan
统计鼠标点击次数, 每次有数据发出时计算累加值并输出, reduce只会在流结束时输出
mouseDown$.pipe(
scan((pre, cur) => ++pre, 0)
).subscribe(console.log)
switchAll / switchMap / switchMapTo
在新的数据到来时, 取消旧的数据流
第四个数据输出时超过了1s, 所以只要最后一个流输出四个值
interval(1000).pipe(
take(3),
map(i => interval(300).pipe(
take(4),
map(x => i + ' ' + x)
)
),
switchAll()
).subscribe(x => console.log('sub', x))
switchMap(v => {
return interval(300).pipe(
take(4),
map(x => v + ' ' + x)
)
})
window
和buffer类似, 输出的是数据流而不是数组
//emit immediately then every 1s
const source = timer(0, 1000);
const example = source.pipe(window(timer(0, 3000)));
const count = example.pipe(scan((acc, curr) => acc + 1, 0));
/*
Window 1:
Window 2:
0
1
2
Window 3:
3
4
5
Window 4:
6
...
*/
const subscribe = count.subscribe(val => console.log(`Window ${val}:`));
const subscribeTwo = example
.pipe(mergeAll())
.subscribe(val => console.log(val));
windowCount
按照元素个数分割数据流
const source = interval(1000);
const example = source.pipe(
//start new window every 4 emitted values
windowCount(4),
tap(_ => console.log('NEW WINDOW!'))
);
const subscribeTwo = example
.pipe(
//window emits nested observable
mergeAll()
/*
output:
"NEW WINDOW!"
0
1
2
3
"NEW WINDOW!"
4
5
6
7
*/
)
.subscribe(val => console.log(val));
windowTime
const source = timer(0, 1000);
const example = source.pipe(
//start new window every 3s
windowTime(3000),
tap(_ => console.log('NEW WINDOW!'))
);
const subscribeTwo = example
.pipe(
//window emits nested observable
mergeAll()
/*
output:
"NEW WINDOW!"
0
1
2
"NEW WINDOW!"
3
4
5
*/
)
.subscribe(val => console.log(val));
windowToggle
传入开始和结束的流, 将期间的数据作为新的流
const source = timer(0, 1000);
//toggle window on every 5
const toggle = interval(5000);
const example = source.pipe(
//turn window on every 5s
windowToggle(toggle, val => interval(val * 1000)),
tap(_ => console.log('NEW WINDOW!'))
);
const subscribeTwo = example
.pipe(
//window emits nested observable
mergeAll()
/*
NEW WINDOW!
NEW WINDOW!
10
NEW WINDOW!
15
16
NEW WINDOW!
20
21
22
NEW WINDOW!
25
*/
)
.subscribe(val => console.log(val));
windowWhen
当内层流有数据时, 将外层流转为新的流
const source = timer(0, 1000);
const example = source.pipe(
//close window every 5s and emit observable of collected values from source
windowWhen(() => interval(5000)),
tap(_ => console.log('NEW WINDOW!'))
);
const subscribeTwo = example
.pipe(
//window emits nested observable
mergeAll()
/*
output:
"NEW WINDOW!"
0
1
2
3
4
"NEW WINDOW!"
5
6
7
8
9
*/
)
.subscribe(val => console.log(val));