异步数据流的响应式编程库Rxjs(八)- 操作符1

41 阅读4分钟

携手创作,共同成长!这是我参与「掘金日新计划 · 8 月更文挑战」的第6天,点击查看活动详情

创建型操作符

of

需要将Observable的值作为参数一个一个的传递进来。

const { Observable, interval, take, from, range, of, map, filter,scan } = require('rxjs');

// 
const source$ = of(6,9,4);

source$.subscribe(x => console.log(x))

console.log('End');

from

从一个数组、类数组对象、Promise、迭代器对象或者类 Observable 对象创建一个 Observable。from的功能比较强大。

const { Observable, interval, take, from, range, of, map, filter,scan } = require('rxjs');

const source$ = from([6,9,4]);

source$.subscribe(x => console.log(x));

console.log('End');

fromEvent

创建一个 Observable,该 Observable 发出来自给定事件对象的指定类型事件。可用于浏览器环境中的DOM事件或Node环境中的EventEmitter事件等。

let click$ = fromEvent(document, 'click');

click$.subscribe((x) => {
   console.log(x);
});

上面的例子,是每点击浏览器窗口一次,控制台就会把点击事件输出出来。那为什么不直接document.addEventListener来的直接呢,如果是这么点需求,的确不必用fromEvent,但是如果还要filter,map等逻辑,用Rxjs就很方便,写起来更优雅,也更加丝滑。

fromPromise

将Promise转换成Observable,这样在编写代码的时候就不用then,catch之类的链式调用了。后面还会讲Rxjs和Promise的主要区别。

let ob1$ = fromPromise(fetch("http://localhost:4000").then((res) => { return res.json(); }));

ob1$.subscribe((data) => {
    console.log(data);
});

interval

使用该操作符创建的Observable可以在指定时间内发出连续的数字。

const { Observable, interval, take, range, of, map, filter,scan } = require('rxjs');

const source$ = interval(1000).pipe(take(5));

source$.subscribe(x => console.log(x))

console.log('End');

// End
// 0
// 1
// 2
// 3
// 4

range

const { Observable, range, of, map, filter,scan } = require('rxjs');

const source$ = range(0, 10);

source$.pipe(
  filter(x => x % 2 === 0),
  map(x => x + x),
  scan((acc, x) => acc + x, 0)
)
.subscribe(x => console.log(x))

console.log('End');

// 0
// 4
// 12
// 24
// 40
// End

转换类操作符

当我们需要对流中的数据就行加工处理时,就可以使用转换类的操作符了。转换类操作符还是有很多的,例如buffer,map,switchMap,mergeMap,concatMap等,这里只介绍一些使用频率最高的几个操作符。

buffer

将过往的值收集到一个数组中去,当另一个observable发出通知时才发出该数组。相当于一个缓冲区,将从上次开始到现在这个期间的数据收集起来,等待一个信号,然后再发射出收集到的数组。

buffer(closingNotifier: Observable<any>): OperatorFunction<T[]>

举个栗子:
buffer相当于一个缓冲区,就像我们在做核酸的时候,10个人一组混采,这10个人就相当于收集到数据的数组,引导员相当于另一个observable,一旦发出信号,就发射出这10个人组成的数组。

const { Observable, fromEvent, fromPromise, interval, take, from, range, of, map, filter,scan, buffer, Subject } = require('rxjs');

const notify$ = new Subject();

const source$ = interval(1000).pipe(buffer(notify$));

setTimeout(() => {
    notify$.next('start');
    
    setTimeout(() => {
        notify$.next('start');
    }, 2000);
}, 3000);

source$.subscribe((x) => {
    console.log(x);
});

console.log('End');

运行效果:

End

[ 0, 1 ]

[ 2, 3 ]

concatMap

concatMap<number, Observable<number>>(project: (value: number, index: number) => Observable<number>): OperatorFunction<number, number> 

将源值投射为一个合并到输出 Observable 的 Observable,以串行的方式等待前一个完成再合并下一个 Observable

const { Observable,concatMap, fromEvent, fromPromise, interval, take, from, range, of, map, filter,scan, buffer, Subject } = require('rxjs');

const source$ = interval(1000);

const res$ = source$.pipe(concatMap(val => interval(1000).pipe(take(3), map((x) => {
    return val * 10;  
}))));

res$.subscribe((x) => {
    console.log(x);
});

console.log('End');

End

0

0

0

10

10

10

20

20

20

...

map

map<T, R>(project: (value: T, index: number) => R): OperatorFunction<T, R>

map操作符和数组中的map方法类似。

const source$ = interval(1000).pipe(take(4));

source$.pipe(map((val) => {
    return val * 3;  
})).subscribe((x)=>{
    console.log(x);
});

console.log('End');

switchMap

将当前流切换到另一个流。map是对流中数据的处理,返回的是处理后的数据;而switchMap是要返回一个新的流。

const { Observable,concatMap, fromEvent, fromPromise, interval, take, from, range, of, map, filter,scan, buffer, Subject, pluck, switchMap } = require('rxjs');

const source$ = interval(1000).pipe(take(3));

source$.pipe(switchMap((x) => {
    if (x === 2) {
        return interval(1000).pipe(take(3));
    }
    else {
        return of(x);
    }
    
})).subscribe((data) => {
    console.log(data);
});

console.log('End');

End

0

1

0

1

2

日常工作中这个操作符使用的还是很多的,例如,通过user account发网络请求去获得userID,然后通过userID再去获取到这个user所有的关注者。

http.getUser$('Tom').pipe(switchMap((user) => {
    return http.getFollowers$(user.id);
})).subscribe((followers) => {
    console.log(followers);
});

下面的写法可以吗?

http.getUser$('Tom').subscribe((user) => {
    http.getFollowers$(user.id).subscribe((followers) => {
        console.log(followers);
    });
});

第二种写法和第一种写法,功能上没有区别,但是如果第二种写法嵌套层级太深的话,代码可读性就变得很差,再次形成了嵌套地狱。

mergeMap

上图中,mergeMap操作符实现的是,一些条件下发出值,一些条件下不发出值。主要是做了一个整合的能力,mergeMap是具有map的功能,map的返回值必须是一个数值,而mergeMap返回的值必须是一个Observable。

const { skip,Observable,concatMap, fromEvent, fromPromise, interval, take, from, range, of, map, filter,scan, buffer, Subject, pluck, switchMap, debounceTime, throttleTime, distinct, mergeMap, isEmpty, empty, EMPTY } = require('rxjs');

const source$ = interval(1000).pipe(take(5));

source$.pipe(mergeMap((val) => {
    return val%2 === 0 ? of(val * 10) : EMPTY;
})).subscribe((x) => {
    console.log(x);
});

console.log('End');

End

0

20

40

pluck

pluck用于选择出每个数据对象上指定的属性值。

const { Observable,concatMap, fromEvent, fromPromise, interval, take, from, range, of, map, filter,scan, buffer, Subject, pluck } = require('rxjs');

const source$ = of({name: 'Tom'}, {name:'Jerry'});

source$.pipe(pluck('name')).subscribe((x) => {
    console.log(x);
});

console.log('End');

Tom

Jerry

End

使用map也是可以的。

scan

累加器操作符,类似于js中的reduce方法。

const { Observable,concatMap, fromEvent, fromPromise, interval, take, from, range, of, map, filter,scan, buffer, Subject, pluck } = require('rxjs');

const source$ = interval(1000).pipe(take(5));

source$.pipe(scan((acc, cur) => (acc+cur))).subscribe((x) => {
    console.log(x);
});

console.log('End');

End

0

1

3

6

10

Rxjs的操作符有很多,本篇文章只是介绍了一部分,后续文章继续哦。