RxJs 仓库
本系列文章
你应该了解
的函数实现与组合应用- 初识RxJs与搭建仓库
- RxJs基础概念与使用
- 操作符篇
- 简易实现Observable
操作符就是在 subscribe
接上一个Observer
之前的一系列数据处理。并且每一个操作都是返回一个全新的Observable对象的函数。
import {Observable} from 'rxjs'
import {map} from 'rxjs/operators'
const onSubscribe=observer=>{
observer.next(1);
observer.next(2)
};
const source$=new Observable<number>(onSubscribe);
source$.pipe(map(x=>x*2)).subscribe(console.log)
分类
Observable
实例,和new Observable
功能一样Observable.create=function(subscribe){
return new Observable(subscribe)
}
Observable
对象import {of} from 'rxjs'
const source$=of(1,2,3);
source$.subscribe(console.log,null,()=>{
console.log('complete')
});
/**
* @description output
* 1
* 2
* 3
* complete
*/
const source$=range(1,3);
source$.subscribe(console.log,null,()=>{
console.log('complete')
});
/**
* @description output
* 1
* 2
* 3
* complete
*/
/**
* initialState: 初始值
* condition: 循环条件
* interate: 迭代条件
* resultSelector: 产生的结果
* scheduler:
*/
generate(initialState, condition, iterate, resultSelector: , scheduler?: SchedulerLike);
举个栗子: 现在我们要产生10以内的所有偶数的平方用。代码如下:
import {generate} from 'rxjs'
const source$=generate(2,
v=>v<10,
v=>v+2,
v=>v*v
);
source$.subscribe(console.log,null,()=>{
console.log('complete')
});
/**
* @description output
* 4
* 16
* 36
* 64
* complete
*/
import {of} from 'rxjs'
import {repeat} from 'rxjs/operators'
const source$=Observable.create(observer=>{
console.log('on subscribe');
setTimeout(() =>observer.next(1), 1000);
setTimeout(() =>observer.next(2), 2000);
setTimeout(() =>observer.next(3), 3000);
setTimeout(() =>observer.complete(), 4000);
return{
unsubscribe:()=>{
console.log('on unsubscribe')
}
}
});
const repeated$=source$.pipe(repeat(2));
repeated$.subscribe(console.log,null,()=>{
console.log('complete')
});
/**
* output:
* 1
* 2
* 3
* 1
* 2
* 3
* complete
*/
值得一提的是,repeat
依赖于上一个Observable完结(调用 complete),如果没有的话,则不会重复产生Observable对象。
自己可以手动去掉 setTimeout(() =>observer.complete(), 4000);
看看结果。
import {empty} from 'rxjs'
const source$=empty()
import { throwError, concat, of } from 'rxjs';
const result$=concat(of(1,2,3),(throwError(new Error('opts'))))
result$.subscribe(console.log,null,console.error)
// Logs:
// 7
// Error: oops!
const result$=never().pipe(startWith(7))
function info(){console.log('will not be called')}
result$.subscribe(console.log,info,info)
异步简单理解就是可以产生时间间隔的数据。
interval:
接受一个数值类型的参数,代表可以产生数据的时间间隔,返回Observable对象就按照该参数产生数据。timer:
import { interval, timer} from 'rxjs'
//从0开始每间隔1s +1
interval(1000).subscribe(console.log)
//和上面效果等价
timer(1000,1000).subscribe(console.log)
// 1s之后产生一个数据 0
timer(1000).subscribe(console.log)
//和上面写法效果等价
const now=new Date();
const later=new Date(now.getTime()+1000);
timer(later).subscribe(console.log)
Observable
对象import { from } from 'rxjs'
//类数组对象转Observable对象
function toObservable(){
return from(arguments)
}
toObservable(1,2,3).subscribe(console.log)
//字符串
from('123').subscribe(console.log)
/**
* output
* 1
* 2
* 3
* /
- fromPromise: 从Promise对象产生数据流
将Promise 传入到 from中
import {from} from 'rxjs'
const promise=Promise.resolve('good');
from(promise)
.subscribe(console.log,console.error,()=>{console.log('complete')})
案例1:浏览器中按钮点击
import {fromEvent} from 'rxjs'
fromEvent(document.getElementById('btn'),'click')
.subscribe(()=>{console.log('btn click')})
案例2: nodejs中事件触发
import EventEmitter from 'events'
import {fromEvent} from 'rxjs'
const emitter=new EventEmitter();
const source$=fromEvent(emitter,'msg');
source$.subscribe(
console.log,
err=>{console.log('catch',err)},
()=>console.log('complete')
);
emitter.emit('msg',1);
emitter.emit('msg',2);
emitter.emit('another-msg','other');
emitter.emit('msg',3);
/**
* output:
* 1
* 2
* 3
* /
值得一提的的是,fromEvent产生的是Hot Observable
,意味着如果我们在订阅前发送消息是不能被接受到的。
import EventEmitter from 'events'
import {fromEvent} from 'rxjs'
const emitter=new EventEmitter();
const source$=fromEvent(emitter,'msg');
emitter.emit('msg',1);
emitter.emit('msg',2);
source$.subscribe(
console.log,
err=>{console.log('catch',err)},
()=>console.log('complete')
);
emitter.emit('another-msg','other');
emitter.emit('msg',3);
// 3
import {ajax} from 'rxjs/ajax'
const obs$ = ajax(`https://api.github.com/404`).pipe(
map(userResponse => console.log('users: ', userResponse)),
catchError(error => {
console.log('error: ', error);
return of(error);
})
);
const observableFactory=()=>of(1,2,3);
const source$=defer(observableFactory)
把多个数据流以首尾相连方式合并
import {of,concat} from 'rxjs'
const source1$=of(1,2,3);
const source2$=of(3,4,5);
const concated$=concat(source1$,source2$)
concated$.subscribe(console.log)
这是一个将高阶的Observable转换为一阶然后依次连接。
高阶Observable 类似于
高阶函数
,在高阶函数中是将函数作为参数然后返回函数。而高阶Observable
是指产生的数据而然是Observable对象。
import {of} from 'rxjs'
import {map,concatAll} from 'rxjs/operators'
const source1$=of(1,2,3);
const higherOrder=source1$
.pipe(
map(num=>of(num))
);
higherOrder.subscribe(console.log)
/**
* 产生三个observable对象,类似这样
* Observable { _isScalar: false, _subscribe: [Function]}
*/
对上面的Observable进行cancatAll
结果如下:
higherOrder.pipe(concatAll()).subscribe(console.log)
/**
* 1
* 2
* 3
* /
把多个数据流中数据以先到先得方式合并,一般用于产生异步数据的合并。
import {timer} from 'rxjs'
import {map} from 'rxjs/operators'
const A$=timer(0,1000).pipe(map(x=>`A-${x}`));
const B$=timer(500,1000).pipe(map(b=>`B-${b}`));
/**
* A-0
* B-0
* A-1
* ...
* /
是一个处理高阶Observable
的merge。
import {of,interval} from 'rxjs'
import {map,mergeAll} from 'rxjs/operators'
const source1$=of(1,2,3);
const higherOrder = source1$.pipe(
map(x => interval(1000).pipe(take(2))
.pipe(map(y => `${x}:${y}`))
)
);
higherOrder.pipe(mergeAll()).subscribe(console.log);
/**
* 1:0
* 2:0
* 3:0
* 1:1
* 2:1
* 3:1
*/
把多个数据流中数据一一对应方式合并,并且会将对应的数据转换成数组传递。
import {pipe,of,zip} from 'rxjs'
import { map } from "rxjs/operators";
const source1$ = of(1, 2, 3);
const A$ = timer(0, 1000).pipe(map(x => `A-${x}`));
zip(A$, source1$).subscribe(console.log, null, () => {
console.log("complete");
});
/**
* output:
* [ 'A-0', 1 ]
* [ 'A-1', 2 ]
* [ 'A-2', 3 ]
* complete
* /
是一个处理高阶Observable
的zip。
import {of,interval} from 'rxjs'
import {map,zipAll} from 'rxjs/operators'
const source1$=of(1,2,3);
const higherOrder = source1$.pipe(
map(x => interval(1000).pipe(take(2))
.pipe(map(y => `${x}:${y}`))
)
);
higherOrder.pipe(zipAll()).subscribe(console.log);
/**
* [ '1:0', '2:0', '3:0' ]
* [ '1:1', '2:1', '3:1' ]
*/
持续合并多个数据流中最新产生的数据
import {pipe,of,zip} from 'rxjs'
import { map } from "rxjs/operators";
const A$ = timer(0, 1000).pipe(map(x => `A-${x}`));
const source1$ = of(1, 2, 3);
combineLatest(source1$,A$).subscribe(
console.log,
null,
()=>{console.log('complete')}
)
/**
* output:
* [ 3, 'A-0' ]
* [ 3, 'A-1' ]
* [ 3, 'A-2' ]
* [ 3, 'A-3' ]
* ...
* /
**A ∗ ∗ 会 每 隔 一 秒 钟 产 生 一 个 最 新 数 据 而 ∗ ∗ s o u r c e **会每隔一秒钟产生一个最新数据 而**source ∗∗会每隔一秒钟产生一个最新数据而∗∗source是同步产生数据,combineLatest
会等待两个数据源都有数据时才会输出并且输出时取最后一条数据(即最新产生的数据)。虽然source ∗ ∗ 已 经 完 结 , 但 是 ∗ ∗ A **已经完结,但是**A ∗∗已经完结,但是∗∗A**还会产生数据所以还会将数据合并后传递给下游。
import { of,combineLatest } from "rxjs";
import { map } from "rxjs/operators";
const source1$ = of(1, 2, 3);
const source2$ = of(3, 4, 5);
combineLatest(source1$,source2$).subscribe(
console.log,
null,
()=>{console.log('complete')}
)
/**
* output:
* [ 3, 3 ]
* [ 3, 4 ]
* [ 3, 5 ]
* complete
* /
combineLatest
订阅 source1$,由于其产生的是同步数据流,在被订阅时会吐出所有数据,最后一个数据时3combineLatest
订阅 source2$complete
功能类似combineLatest
但是它只能由上游Observable
对象驱动下游推送数据。
import {withLatestFrom } from "rxjs/operators";
const source1$ = of(1, 2, 3);
const source2$ = of(3, 4, 5);
source1$
.pipe(withLatestFrom(source2$,(a,b)=>`${a}-${b}`))
.subscribe(
console.log,
null,
()=>console.log('complete')
)
/**
* output:
* 1-5
* 2-5
* 3-5
* complete
* /
是一个处理高阶Observable
的combineLatest。
import {of,interval} from 'rxjs'
import {map,combineAll} from 'rxjs/operators'
const source1$=of(1,2,3);
const higherOrder = source1$.pipe(
map(x => interval(1000).pipe(take(2))
.pipe(map(y => `${x}:${y}`))
)
);
higherOrder.pipe(combineAll()).subscribe(console.log);
/**
* [ '1:0', '2:0', '3:0' ]
* [ '1:1', '2:0', '3:0' ]
* [ '1:1', '2:1', '3:0' ]
* [ '1:1', '2:1', '3:1' ]
*/
从多个数据流中选取第一个产生内容的数据流,之后产生的数据则会被退订。
import {race} from 'rxjs'
const source1$ = of(1, 2, 3);
const source2$ = of(3, 4, 5);
race(source1$, source2$).subscribe(console.log, null, () =>
console.log("complete")
);
/**
* 1
* 2
* 3
* complete
* /
在数据流前面添加一个指定数据
import { map, startWith } from "rxjs/operators";
const source1$ = of(1, 2, 3);
source1$
.pipe(map(x=>x*2),startWith('start'))
.subscribe(console.log)
/**
* start
* 2
* 4
* 6
* /
import { of,concat } from "rxjs";
const source1$ = of(1, 2, 3);
concat(of('start'),source1$)
.subscribe(console.log)
只获取多个数据流中最后产生的那个数据,也就是说它会等所有数据都完结后,然后把产生的最后一条数据合并。
import {of} from 'rxjs'
import { take } from "rxjs/operators";
const source1$ = of(1, 2, 3);
const source3$ = timer(0, 1000).pipe(map(x => `A-${x}`)).pipe(take(3));
forkJoin(source1$,source3$)
.subscribe(console.log)
/**
* 等待3秒钟输出:
* [3,'A-2']
* /
统计数据流中产生的所有数据个数
import { of,concat } from "rxjs";
import { count } from "rxjs/operators";
const source1$ = of(1, 2, 3);
const source2$ = of(3, 4, 5);
concat(source1$,source2$).pipe(count())
.subscribe(console.log)
获得数据流中最大或者最小的数据
import { of } from "rxjs";
import { min } from "rxjs/operators";
const person=of(
{name:'c',age:12},
{name:'chen',age:24},
{name:'wu',age:17},
{name:'yc',age:37},
)
person.pipe(min((a,b)=>a.age-b.age))
.subscribe(console.log)
对数据流中所有数据进行规约操作,其功能与js中reduce一样只不过操作对象变成成了observable
import { of } from "rxjs";
import { min } from "rxjs/operators";
const person=of(
{name:'c',age:12},
{name:'chen',age:24},
{name:'wu',age:17},
{name:'yc',age:37},
)
person.pipe(reduce((acc,cur)=>(acc+cur.age),0))
.subscribe(console.log)
判断是否所有数据满足某个条件
import { of } from "rxjs";
import { every } from "rxjs/operators";
const source1$ = of(1, 2, 3);
source1$.pipe(every(x=>x>1))
.subscribe(console.log)
//false
找到第一个满足判定条件的数据,find返回第一个元素,findIndex返回第一个元素的坐标
import { of } from "rxjs";
import { find,findIndex } from "rxjs/operators";
const source1$ = of(1, 2, 3);
source1$.pipe(find(x=>x>1))
.subscribe(console.log)
//2
source1$.pipe(findIndex(x=>x>1))
.subscribe(console.log)
//1
判定一个数据流是否不包含任何数据即Observable对象不再吐出任何数据。
import { of,never, } from "rxjs";
import {isEmpty } from "rxjs/operators";
const source1$ = of(1, 2, 3);
source1$.pipe(isEmpty())
.subscribe(console.log)
// false
never().pipe(isEmpty())
.subscribe(console.log)
//不会产生任何结果
对于never,isEmpty不会产生任何结果,因为它的上游Observable对象
即不吐出任何数据证明它非空,也不能证明完结所以就一直等待。
如果一个数据流为空就默认产生一个指定数据
import {of} from 'rxjs'
import {defaultIfEmpty } from "rxjs/operators";
of().pipe(defaultIfEmpty('this is default'))
.subscribe(console.log)
import {of} from 'rxjs'
import {filter} from 'rxjs/operators'
const source1$ = of(1, 2, 3);
source1$.pipe(filter(x=>x>2))
.subscribe(console.log)
//3
first:
last:
与first正好相反,返回最后一个符合条件的数据。
import {of} from 'rxjs'
import {first} from 'rxjs/operators'
const source1$ = of(1, 2, 3);
source1$.pipe(first(x=>x>1))
.subscribe(console.log)
//2
source1$.pipe(first())
.subscribe(console.log)
//1
接受一个参数,该参数决定从上游数据中拿几个数据。
import {of} from 'rxjs'
import {take} from 'rxjs/operators'
const source1$ = of(1, 2, 3);
source1$.pipe(take(2))
.subscribe(console.log)
// 1
// 2
让我们可以用Observable对象
来控制另一个Observable对象
的数据产生。
案例: 每隔一秒输出一个递增整数,三秒后结束。
import {of,timer,interval} from 'rxjs'
import {takeUntil} from 'rxjs/operators'
const source1$ = interval(1000);
source1$.pipe(takeUntil(timer(2500)))
.subscribe(
console.log,
console.error,
()=>{console.log('complete')}
)
// 0
// 1
// complete
跳过前N个之后开始拿即设定起点位置。
import {of} from 'rxjs'
import {skip} from 'rxjs/operators'
const source1$ = of(1, 2, 3);
source1$.pipe(skip(2))
.subscribe(console.log)
//3
后缀Time表示毫秒数的时间。而不带Time后缀的操作符是利用另一个Observable对象来控制如何抛弃来自上游Observable对象的数据。
表示每隔毫秒时间范围内抛弃所有上游传递下来的数据,即这样保证了时间范围内只给下游传递唯一一个数据。
import { interval } from "rxjs";
import {throttleTime} from 'rxjs/operators'
interval(1000)
.pipe(throttleTime(2000))
.subscribe(console.log,null,()=>console.log('complete'));
/**
* 0
* 2
* 4
* ...
*/
防抖:表示毫秒范围内不产生任何其它数据时才把数据传递给下游,如果在此时间产生数据则重新开始计时。
import { interval } from "rxjs";
import {debounceTime} from 'rxjs/operators'
interval(1000)
.pipe(debounceTime(2000))
.subscribe(console.log,null,()=>console.log('complete'));
/**
* 不会产生任何数据,以为每隔一秒都会产生新的数据,deounce是要等待两秒之内不产生任何数据采用将数据传递给下流。
*/
它与throttle类似,不过throttle是把第一个数据传递给下游;而audit是把最后一个数据传给下游。
import { interval } from "rxjs";
import {throttleTime} from 'rxjs/operators'
interval(1000)
.pipe(throttleTime(2000))
.subscribe(console.log,null,()=>console.log('complete'));
/**
* 1
* 3
* 5
* ...
*/
只返回从没有出现过的数据,上游同样的数据只有第一次产生时会传递给下游,其余的都被丢弃掉了。
import { of } from "rxjs";
import {distinct} from 'rxjs/operators'
of(1,1,2,2,3,4,6,5,5,5,6)
.pipe(distinct())
.subscribe(console.log,null,()=>console.log('complete'));
/**
* 1
* 2
* 3
* 4
* 6
* 5
* complete
* /
import { of } from 'rxjs';
import { distinct } from 'rxjs/operators';
interface Person {
age: number,
name: string
}
of<Person>(
{ age: 4, name: 'Foo'},
{ age: 7, name: 'Bar'},
{ age: 5, name: 'Foo'},
).pipe(
distinct((p: Person) => p.name),
)
.subscribe(x => console.log(x));
// displays:
// { age: 4, name: 'Foo' }
// { age: 7, name: 'Bar' }
拿到数据和上一个数据进行比较,如果重复则过滤掉。
import { of } from "rxjs";
import {distinctUtilChanged} from 'rxjs/operators'
of(1,1,2,2,1)
.pipe(distinctUtilChanged())
.subscribe(console.log,null,()=>console.log('complete'));
/**
* 1
* 2
* 1
* complete
* /
检查上游是否只有一个满足对于条件的数据,如果是则传递该数据,否则抛出异常。
import { of } from "rxjs";
import {single} from 'rxjs/operators'
of(1,2)
.pipe(single(x=>x===1))
.subscribe(console.log,null,()=>console.log('complete'));
/**
* 1
* complete
*/
// 如果修改of(1,2,1) 则会报Error Sequence contains more than one element
将上游数据一一通过函数F()转换后传递给下游:A -->f(A)
将每个元素映射函数产生新的数据
将数据流中每个元素映射为同一个数据
提取数据流中每个数据的某个字段,当获取数据不存在则返回undefined。
import {of} from 'rxjs'
import {map,mapTo,pluck} from 'rxjs/operators'
of(1,2,3)
.pipe(map(x=>x*2))
.subscribe(console.log,null,()=>console.log('complete'));
/**
* 2
* 4
* 6
* complete
*/
of(1,2,3)
.pipe(mapTo('A'))
.subscribe(console.log,null,()=>console.log('complete'));
/**
* A
* A
* A
* complete
*/
of(
{name:'c1',age:12},
{name:'c2',age:32},
{name:'c3',age:24},
{name:'c4',age:53},
)
.pipe(pluck('age'))
.subscribe(console.log,null,()=>console.log('complete'));
/**
* 12
* 32
* 24
* 53
* complete
*/
该策略就是将上游在一段时间内产生的数据放在一个数据集合里然后一次性丢给下游。
数据集合类型:
根据时间缓存上游数据。
import {interval} from 'rxjs'
import {bufferTime} from 'rxjs/operators'
interval(1000)
.pipe(bufferTime(3000))
.subscribe(console.log,null,()=>console.log('complete'));
/**
* [0,1]
* [2,3,4]
* [5,6,7]
*/
根据个数来缓存上游数据。
import {interval} from 'rxjs'
import {bufferCount} from 'rxjs/operators'
interval(1000)
.pipe(bufferCount(3))
.subscribe(console.log,null,()=>console.log('complete'));
/**
* [0,1,2]
* [3,4,5]
* [6,7,8]
*/
捕获并处理上游产生的异常错误
import {range,of} from 'rxjs'
import {map,catchError,take} from 'rxjs/operators'
const throwOnNumber=value=>{
if(value===2){
throw new Error('number equaly 2')
}
return value
}
range(1,5)
.pipe(
map(throwOnNumber),
catchError((err,catch$)=>of(6))
)
.subscribe(console.log,null,()=>console.log('complete'));
/**
* 1
* 6
* complete
* /
当上游产生错误时进行重试
案例:第一次失败后再重试2次
import {range,of} from 'rxjs'
import {map,catchError,retry} from 'rxjs/operators'
range(1,5)
.pipe(
map(throwOnNumber),
retry(2),
catchError((err,catch$)=>of(6)),
)
.subscribe(console.log,null,()=>console.log('complete'));
/*
1
1
1
6
complete
*/
retryWhen:用于控制重试次数和节奏
案例:重试2次,每次重试前延迟2秒钟
import {range,of} from 'rxjs'
import {map,catchError,retryWhen,scan} from 'rxjs/operators'
const throwOnNumber=value=>{
if(value===2){
throw new Error('number equaly 2')
}
return value
}
range(1,5)
.pipe(
map(throwOnNumber),
retryWhen(err$=>err$.pipe(
scan((errorCount,err)=>{
if(errorCount>=2){
throw err
}
return errorCount+1;
},0),
delay(2000)
)),
// catchError((err,catch$)=>of(6)),
)
.subscribe(console.log,null,()=>console.log('complete'));
/**
* 1
* 1
* 1
* Error: number equaly 2
*/
无论是否出错都要进行一些操作
播放内容:
通过Subject
模拟一个多播场景:
const obs$=interval(1000)
.pipe(take(3));
const subject=new Subject();
obs$.subscribe(subject)
subject.subscribe(value=>console.log('observer 1:',value))
setTimeout(() => {
subject.subscribe(value=>console.log('observer 2:',value))
}, 1000);
/**
* observer 1: 0
* observer 1: 1
* observer 2: 1
* observer 1: 2
* observer 2: 2
*/
import {interval,Subject} from 'rxjs'
import {take,multicast} from 'rxjs/operators'
const tick$=
interval(1000)
.pipe(
take(3),
multicast(()=>new Subject())
)
tick$.subscribe(value=>console.log('observer 1:',value))
setTimeout(() => {
tick$.subscribe(value=>console.log('observer 2:',value))
}, 1000);
//订阅数据
tick$.connect()
多播上游最后一个数据
import {interval,Subject} from 'rxjs'
import {take,publishLast} from 'rxjs/operators'
const tick$=
interval(1000)
.pipe(
take(3),
publishLast()
)
tick$.connect()
tick$.subscribe(value=>console.log('observer 1:',value))
setTimeout(() => {
tick$.subscribe(value=>console.log('observer 2:',value),null,()=>console.log('complete'))
}, 5000);
/**
* observer 1: 2
* observer 2: 2
* complete
*/
重播即将上游数据重新走一遍,参数表示能缓存的大小。
import {interval,Subject} from 'rxjs'
import {take,publishReplay} from 'rxjs/operators'
const tick$=
interval(1000)
.pipe(
take(3),
publishReplay(2)
)
tick$.connect()
tick$.subscribe(value=>console.log('observer 1:',value))
setTimeout(() => {
tick$.subscribe(value=>console.log('observer 2:',value),null,()=>console.log('complete'))
}, 5000);
/**
* observer 1: 0
* observer 1: 1
* observer 1: 2
* observer 2: 1
* observer 2: 2
* complete
*/