中文官网rxjs文档:https://cn.rx.js.org/manual/overview.html
操作符-中文:https://rxjs-cn.github.io/learn-rxjs-operators/operators/multicasting/share.html
操作符-英文:https://www.learnrxjs.io/operators/multicasting/share.html
腾讯云:https://cloud.tencent.com/developer/doc/1257
个人:https://www.jianshu.com/p/16be96d69143
Observable的创建:
https://segmentfault.com/a/1190000008809168
https://blog.csdn.net/xiangzhihong8/article/details/79157986
https://www.imooc.com/article/70323
以下参考:
https://segmentfault.com/a/1190000012252368
什么是RxJS?
RxJS
是ReactiveX
编程理念的JavaScript
版本。ReactiveX
来自微软
,它是一种针对异步数据流
的编程。简单来说,它将一切数据
,包括HTTP请求
,DOM事件
或者普通数据
等包装成流的形式
,然后用强大丰富的操作符对流进行处理
,使你能以同步编程
的方式处理异步数据,并组合不同的操作符来轻松优雅的实现你所需要的功能。
Rxjs与promise的区别
RxJS
中的流以Observable
对象呈现,获取数据需要订阅Observable
,形式如下:
// Observabel:subscribe,catch
const ob = http$.getSomeList(); //getSomeList()返回某个由`Observable`包装后的http请求
ob.subscribe((data) => console.log(data));
//在变量末尾加$表示Observable类型的对象。
以上与Promise
类似:
// Promise:then,catch
const promise = http.getSomeList(); // 返回由`Promise`包装的http请求
promise.then((data) => console.log(data));
实际上Observable
可以认为是加强版的Promise
,它们之间是可以通过RxJS
的API互相转换的:
// 转换方法:observable.toPromise(),Observable.fromPromise(promise)
const ob = Observable.fromPromise(somePromise); // Promise转为Observable
const promise = someObservable.toPromise(); // Observable转为Promise
因此可以在Promise
方案的项目中安全使用RxJS
,并能够随时升级到完整的RxJS方案。
RxJS的引入方式
不要用 import { Observable } from 'rxjs'
这种方式导入,这会导入整个rxjs库,按需导入的方式如下:
import { Observable } from 'rxjs/Observable' // 导入类
import 'rxjs/add/operator/map' // 导入实例操作符
import 'rxjs/add/observable/forkJoin' // 导入类操作符
概念
Observable
Observer
Operator
subscription
Observable
被称为可观察序列,简单来说数据就在Observable
中流动,你可以使用各种operator
对流进行处理,例如:
const ob = Observable.interval(1000); // ob就是Observable,里面是流
ob.take(3).map(n => n * 2).filter(n => n > 2);
第一步代码我们通过类方法interval创建了一个Observable
序列,ob作为源会每隔1000ms发射一个递增的数据,即0 -> 1 -> 2。
第二步我们使用操作符对流进行处理。
上面我们已经使用同步编程创建好了一个流的处理过程,但此时ob作为源并不会立刻发射数据,如果我们在map中打印n是不会得到任何输出的,因为ob作为Observable序列必须被“订阅”
才能够触发上述过程,也就是subscribe
(发布/订阅模式)。
const ob = Observable.interval(1000);
let subscription = ob.take(3).map(n => n * 2).filter(n => n > 0).subscribe(n => console.log(n));
结果:
2 //第2秒
4 //第3秒
我自己的版本:
import {interval} from 'rxjs/observable/interval';
import 'rxjs/add/operator/take';
import 'rxjs/add/operator/map';
import 'rxjs/add/operator/filter';
let ob = interval(1000);
console.log(ob);
ob.take(3)
.map(n=>{
console.log(n);
return n*2;
})
.filter(n=>n>2)
.subscribe(n=>{
console.log(n);
})
上面代码中我们给subscribe
传入了一个函数,这其实是一种简写,subscribe
完整的函数签名如下:
ob.subscribe({
next: d => console.log(d),
error: err => console.error(err),
complete: () => console.log('end of the stream')
})
直接给subscribe
传入一个函数会被当做是next
函数。
这个完整的包含3个函数的对象被称为observer
(观察者),表示的是对序列结果的处理方式。
next
表示数据正常流动,没有出现异常;
error
表示流中出错,可能是运行出错,http报错等等;
complete
表示流结束,不再发射新的数据。
在一个流的生命周期中,error
和complete
只会触发其中一个,可以有多个next(表示多次发射数据),直到complete或者error。
observer.next
可以认为是Promise中then的第一个参数;
observer.error
对应第二个参数或者Promise的catch。
RxJS
同样提供了catch操作符,err流入catch后,catch必须返回一个新的Observable。被catch后的错误流将不会进入observer的error函数,除非其返回的新observable出错。
// catch
Observable.of(1).map(n => n.undefinedMethod()).catch(err => {
// 此处处理catch之前发生的错误
return Observable.of(0); // 返回一个新的序列,该序列成为新的流。
});
创建可观察序列(即Observable)
创建一个序列有很多种方式,我们仅列举常用的几种:
Observable.of(...args)
将普通JavaScript数据转为可观察序列
var $source= of('111','222');
$source.subscribe({
next:(res)=>{
console.log(res); // 111 222
},
error:()=>{
console.log('error');
},
complete:()=>{
console.log('complete'); // 会执行的。
}
})
- from
from的参数必须是一个类数组(set,iterator等),其他和of一样
var $source= from(['111','222']);
$source.subscribe({
next:(res)=>{
console.log(res); // 111 222
},
error:()=>{
console.log('error');
},
complete:()=>{
console.log('complete'); // 会执行的。
}
})
Observable.fromPromise(promise)
将Promise转化为Observable
var $source= fromPromise(new Promise((resolve,reject)=>{
// 异步接口:
setTimeout(()=>{
resolve('hello rxjs');
},3000)
}))
$source.subscribe({
next:(res)=>{
console.log(res); // hello rxjs
},
error:()=>{
console.log('error');
},
complete:()=>{
console.log('complete'); // 会执行
}
})
Observable.fromEvent(elment, eventName)
从DOM事件创建序列,eg:Observable.fromEvent($input, 'click')
绑定事件:
<input type="text" id="md">
Observable.fromEvent(document.querySelector("#md"),'keyup') // 为dom元素绑定'keyup'事件
.debounceTime(250) // 防抖动
.pluck('target','value') // 取值
.subscribe((res=>{
console.log(res); // 返回的是input的value值
}))
在subscribe前面还有一个: .switchMap(url => Http.get(url)) // 将当前输入流替换为http请求
,不知道干嘛的。
注:是fromEvent,不是formEvent(from)。
Observable.ajax(url | AjaxRequest)
发送http请求Observable.create(subscribe)
这个属于万能的创建方法
var $observable= Observable.create(function (observer) {
observer.next('111');
observer.next('222');
})
$observable.subscribe(res=>{
console.log(res);
})
这里值得一提的是rxjs的subscribe是同步执行的,例如下边这段代码:
var $observable= Observable.create(function (observer) {
observer.next('111');
observer.next('222');
})
console.log('start');
$observable.subscribe(res=>{
console.log(res);
})
console.log('end');
结果:
start
111
222
end
- throw
var $source = Observable.throw('ksbk');
$source.subscribe({
next: (res) => {
console.log(res);
},
complete: () => {
console.log('complete');
},
error: () => {
console.log('error执行了'); // 只执行了它
}
})
- interval 和 timer
var $source = interval(1000);
$source.take(3).subscribe({
next:(res)=>{
console.log(res); // 0 1 2
},
complete:()=>{
console.log("complete"); // 结束的时候,会执行
},
error:()=>{
console.log("error");
}
})
timer
有两个参数,第一个参数表示到发送第一个值的间隔时间,第二个参数表示从发送第二个参数开始,每发送一个值的间隔时间;如果第二个参数为空则发送第一个参数后,终止,执行complete函数。
var $source = timer(1000);
$source.subscribe({
next: (res) => {
console.log(res); // 0
},
complete: () => {
console.log("complete"); // 结束的时候,会执行
},
error: () => {
console.log("error");
}
})
var $source = timer(1000,5000);
$source.take(3).subscribe({
next: (res) => {
console.log(res); // 0 1 2 (隔1s打印0,然后每隔5s打印2,3)
},
complete: () => {
console.log("complete"); // 结束的时候,会执行
},
error: () => {
console.log("error");
}
})
合并序列
合并序列也属于创建序列的一种,例如有这样的需求:进入某个页面后拿到了一个列表,然后需要对列表每一项发出一个http请求来获取对应的详细信息,这里我们把每个http请求作为一个序列,然后我们希望合并它们。
合并有很多种方式(concat,merge,forkJoin),例如:
- N个请求按顺序串行发出(前一个结束再发下一个);
- N个请求同时发出,对于每一个到达就触发一次回调。
- N个请求同时发出并且要求全部到达后合并为数组,触发一次回调;
如果不用RxJS,我们会比较难处理这么多情形,不仅实现麻烦,维护更麻烦,下面是使用RxJS对上述需求的解决方案:
const ob1 = Observable.ajax('api/detail/1');
const ob2 = Observable.ajax('api/detail/2');
// ...
const obs = [ob1, ob2...];
// 分别创建对应的HTTP请求。
- N个请求按顺序串行发出(前一个结束再发下一个)
Observable.concat(...obs).subscribe(detail => console.log('每个请求都触发回调'));
- N个请求同时并行发出,对于每一个到达就触发一次回调
Observable.merge(...obs).subscribe(detail => console.log('每个请求都触发回调'));
- N个请求同时发出并且要求全部到达后合并为数组,触发一次回调
Observable.forkJoin(...obs).subscribe(detailArray => console.log('触发一次回调'));
注:在代码中测试:结果确实是如此。
ob1返回list1,ob2:返回list2,那么forkJoin的时候,返回为list,里面为list1和list2。
import { Observable } from 'rxjs/Rx';
instead of import { Observable } from 'rxjs/Observable';
// 结果的结构:
[list1,list2]
注:需求:页面一进来可以执行多个接口,我们在所有接口最后都执行完了,再去做其他操作。
使用RxJS实现搜索功能
<nz-input [(ngModel)]="deviceName" (keyup)="deviceNameChange();"></nz-input>
import {Subject} from 'rxjs/Subject';
import 'rxjs/add/operator/debounceTime';
import 'rxjs/add/operator/distinctUntilChanged';
private searchTerms = new Subject<string>();
ngOnInit() {
this.searchTerms
.debounceTime(300) // 每次点击后等待300毫秒
.distinctUntilChanged() // 如果下一个搜索词与之前相同就忽略
.subscribe(val => {
this.getDevices(val);
})
}
/**
* 模糊搜索功能
*/
deviceName = ""; // deviceName模糊搜索
// 输入框改变值的时候:
deviceNameChange() {
console.log('change事件是执行了');
this.searchTerms.next(this.deviceName);
}
getDevices(val) {
console.log("调用接口,获取设备列表下拉");
// ajax
}
个人总结的常用操作符:
- 类操作符(通常为合并序列或从已有数据创建序列)
合并:concat
,merge
,forkJoin
创建:of
,from
,fromPromise
,fromEvent
,ajax
,throw
,interval
和timer
等。 - 实例操作符(对流中的数据进行处理或者控制流程)
map
,filter
,switchMap
,toPromise
,catch
,take
,takeUntil
,timeout
,debounceTime
,distinctUntilChanged
,pluck
。
以下参考: https://www.jianshu.com/p/16be96d69143
分类:
创建observable类(上一篇都有)
create,of,from,fromPromise,fromEvent,interval,timer,等
选择器类:
take,first,last,takeLast,
first
:取第一个数然后结束,和take(1)效果一样。
takeLast
和take
用法一样,区别是该方法是取后边几个值,eg:
var $source = interval(3000).pipe(take(5),takeLast(2));
$source.subscribe({
next:(res)=>{
console.log(res); // 3 4 (3 4 complete在15s后,同时打印的)
},
complete:()=>{
console.log("complete"); // 结束的时候,会执行
},
error:()=>{
console.log("error");
}
})
last
是takeLast(1)
的简写,目的是取最后一个值。
控制数据流类
takeUntil
参数为一个Observable,当参数Observable订阅发生,终止takeUntil绑定的observable。
下边这个案例,当点击body时就会终止订阅。
var $click = fromEvent(document.getElementsByTagName('body'), 'click');
var $source = interval(1000).pipe(takeUntil($click));
$source.subscribe({
next: (res) => {
console.log(res); // 0 1 2 ... 页面click后,就停止了
},
complete: () => {
console.log("complete"); // 结束的时候,会执行
},
error: () => {
console.log("error");
}
})
skip
// 例子中就跳过了前三个值,但是要注意的是获取前三个值的时间还是要等待的
let $source = interval(1000).pipe(skip(3)); // 跳过前三个
startWith
// 塞一个初始值给Observable
let $source = interval(1000).pipe(startWith(1000));
let source = of(1,2,3,4,5,6,7,8,9);
source.pipe(skip(3),startWith(999),take(4)).subscribe(res=>{
console.log(res); // 999 4 5 6
})
concat
concat和concatAll效果是一样的,区别在于 concat要传递参数,参数必须是Observable类型。
let source1 = interval(1000).pipe(take(3));
let source2 = of(3);
let source3 = of(4, 5);
// let source= source1.pipe(concat(source2,source3));
let source = Observable.concat(source1, source2, source3);
source.subscribe(res => {
console.log(res); // 前两个是按秒来,第三秒和其他的一起返回(0 1 2 3 4 5)。
})
merge
merge使用方式和concat一样,区别就是merge处理的Observable是异步执行的,没有先后顺序。
let source1 = interval(1000).pipe(take(3));
let source2 = of(3);
let source3 = of(4, 5);
// let source= source1.pipe(merge(source2,source3));
let source = Observable.merge(source1, source2, source3);
source.subscribe(res => {
console.log(res); // 先同时返回3,4,5,然后定时返回0 1 2。
})
forkJoin
let source1 = interval(1000).pipe(take(3));
let source2 = of(3);
let source3 = of(4, 5);
// let source= source1.pipe(forkJoin(source2,source3));
let source = Observable.forkJoin(source1, source2, source3);
source.subscribe(res => {
console.log(res); // 三秒后返回:[2, 3, 5] (每个序列的最后一个的集合)
})
delay
delay会将observable第一次发出订阅的时间延迟,如下:
let source = interval(1000).pipe(take(5), delay(5000));
source.subscribe(res => {
console.log(res); // 5秒后,在定时执行0 1 2 3 4 5
})
debounceTime
debounce
在每次收到元素,他会先把元素 cache 住并等待一段时间,如果这段时间内已经没有收到任何元素,则把元素送出;如果这段时间内又收到新的元素,则会把原本 cache 住的元素释放掉并重新计时
,不断反复。
let source = interval(300).pipe(take(6), debounceTime(1000));
source.subscribe(res => {
console.log(res); // 返回最后一个5。
})
throttleTime
跟 debounce 的不同是 throttle 先会开放送出元素,等到有元素被送出就会沉默一段时间,等到时间过了又会继续发送元素,防止某个事件频繁触发,影响效率。(时间是累计时间,不会清零)
let source = interval(300).pipe(take(10), throttleTime(1000));
source.subscribe(res => {
console.log(res); // 每隔一秒返回一个,共: 0 4 8
})
distinct 和 distinctUntilChanged
distinct
英 [dɪˈstɪŋkt] 美 [dɪˈstɪŋkt]
adj:截然不同的;有区别的;不同种类的;
distinct会和已经拿到的数据比较过滤掉 重复的元素如下:
let source = from(['a','b','c','a','b']).pipe(
zip(interval(1000),(x,y)=>x), // 如果是y,那么返回的是数组的索引。
distinct()
);
source.subscribe(res=>{
console.log(res); // 每隔一秒返回:a b c
})
distinct第一个参数是个函数,函数返回值就是distinct比较的值,eg:
let arr=[
{name:'a',value:1},
{name:'b',value:2},
{name:'c',value:3},
{name:'a',value:4},
{name:'c',value:5},
]
let source = from(arr).pipe(
zip(interval(1000),(x,y)=>x), // 如果是y,那么返回的是interval的值(0 1 2 3...)。
distinct(x=>x.name) // keySelector筛选哪一项
);
source.subscribe(res=>{
console.log(res); // 每隔一秒返回:a b c的对象(前三个)
})
distinctUntilChanged
与distinct
不同之处就是,distinctUntilChanged
只会比较相邻两次输入,例子如下:
let arr=['a', 'b', 'c', 'c', 'b'];
let source = from(arr).pipe(
zip(interval(1000),(x,y)=>x),
distinctUntilChanged()
);
source.subscribe(res=>{
console.log(res); // 每隔一秒返回:a b c b。c后面会停顿一秒(因为还有一个c呢)
})
改变数据流结构
concatAll和mergeAll
concatAll
let source1 = interval(1000).pipe(take(3));
let source2 = of(3);
let source3 = of(4, 5);
// let source = Observable.concat(source1, source2, source3);
let source = of(source1, source2, source3);
source.pipe(concatAll()).subscribe(res => {
console.log(res); // 前两个是按秒来,第三秒和其他的一起返回(0 1 2 3 4 5)。
})
// 如果没有过滤:res:Observable,ScalarObservable,ArrayObservable。
与concat一样的。只是没有参数,在of里面。
mergeAll
let source1 = interval(1000).pipe(take(3));
let source2 = of(3);
let source3 = of(4, 5);
let source = of(source1, source2, source3);
source.pipe(mergeAll()).subscribe(res => {
console.log(res); // 先3,4,5,然后按秒返回 0 1 2。
})
let click =fromEvent(document.querySelector('body'),'click');
let source= click.pipe(map(e=>interval(1000)),mergeAll())
source.subscribe(res=>{
console.log(res);
})
// 每点击页面一次,就会开始一个计时器。N次点击,会有N个定时器执行(如果是concat:执行完一个才会执行下一个,所以最好加上take())。
数据操作类
map
和JavaScript中的map一样
var $source = interval(1000).pipe(map(v=>v*3));
$source.subscribe(res=>{
console.log(res); // 0 3 6 9 ...
})
// 必须是一个一个来,要是用of的话,那么就报错了!
var $source=from([
{name:'a',val:1,id:111},
{name:'b',val:2,id:222},
{name:'c',val:3,id:333},
])
$source.pipe(map((v,k)=>{
return v.val*2;
})).subscribe(res=>{
console.log(res); // 2 4 6
})
filter
// 必须是一个一个来,要是用of的话,那么就报错了!
var $source=from([
{name:'a',val:1,id:111},
{name:'b',val:2,id:222},
{name:'c',val:3,id:333},
{name:'d',val:4,id:444},
])
$source.pipe(filter((v,k)=>{
return v.val>2;
})).subscribe(res=>{
console.log(res); // 第三个和第四个对象。
})
mapTo
// 将参数转换为一个固定值
var $source=from([
{name:'a',val:1,id:111},
{name:'b',val:2,id:222},
{name:'c',val:3,id:333},
{name:'d',val:4,id:444},
])
$source.pipe(mapTo(21)).subscribe(res=>{
console.log(res); // 21,21,21,21
})
停止订阅
public subscription:any; // 订阅的实例
ngOnInit() {
let $source = interval(1000).pipe(skip(3));
this.subscription=$source.subscribe({
next:(res)=>{
console.log(res);
},
error:()=>{
console.log('error');
},
complete:()=>{
console.log('complete');
}
})
}
ngOnDestroy(){
console.log("销毁");
console.log( this.subscription);
this.subscription.unsubscribe();
}
协调多个Observable类
combineLatest
协调过个observable,参数Observable中有一个发生变化都会发起订阅(前提是每个observable都有值)。
const timerOne = timer(1000, 4000); // timerOne 在1秒时发出第一个值,然后每4秒发送一次
const timerTwo = timer(2000, 4000); // timerTwo 在2秒时发出第一个值,然后每4秒发送一次
const timerThree = timer(3000, 4000);// timerThree 在3秒时发出第一个值,然后每4秒发送一次
// 当一个 timer 发出值时,将每个 timer 的最新值作为一个数组发出
const combined = combineLatest(timerOne, timerTwo, timerThree);
const subscribe = combined.subscribe(latestValues => {
console.log("最新的值:",latestValues); // 依次为:[0,0,0],[1,0,0],[1,1,0],[1,1,1],[2,1,1]...
// 从 timerValOne、timerValTwo 和 timerValThree 中获取最新发出的值
const [timerValOne, timerValTwo, timerValThree] = latestValues;
console.log(
`Timer One Latest: ${timerValOne},Timer Two Latest: ${timerValTwo},Timer Three Latest: ${timerValThree}`
);
/*示例:
timerOne first tick: 'Timer One Latest: 1, Timer Two Latest:0, Timer Three Latest: 0
timerTwo first tick: 'Timer One Latest: 1, Timer Two Latest:1, Timer Three Latest: 0
timerThree first tick: 'Timer One Latest: 1, Timer Two Latest:1, Timer Three Latest: 1
// 递增...
*/
}
);
有一个值发生了变化,那么就会触发一次订阅。
当conbineLatest没有传入第二个参数,返回的订阅值是个数组,但是conbineLatest可以传入第二个参数,在发给Observabler进行数据处理。eg:
const source1 = interval(1000).pipe(take(20));
const source2 = interval(3000).pipe(take(20));
const source3 = of(4, 5);
const example = source1.pipe(combineLatest(source2, (x, y) => {
// console.log(x,y); // x:是source1的最新值;y:是source2的最新值;
// 1 0; 2 0; 3 0; 4 0;
// 4 1; 5 1; 6 1; 7 1;
// 7 2; 8 2; 9 2; 10 2;
// 10 3;.........19 19;
// (最后x不变了,只有y变)
return x + y
}));
example.subscribe({
next: value => {
console.log(value); // 1,2,3,4,5,6.........38。
// 前两个一起执行,每隔一秒再执行两个,然后循环。
},
error: err => {
console.log("Error: " + err);
},
complete: () => {
console.log("complete");
}
});
如果我将source2间隔改为5s,那么结果为:
// 3 0 4 0;5 0 ; 6 0;7 0;8 0;
// 8 1 9 1; 10 1;11 1; 12 1; 13 1;
// 13 2 14 2;15 2;16 2 17 2; 18 2;
// ......
分析:最开始的时候,source1和source2都没有值,所以不执行。
source1为0的时候,source2无值,不执行。
source为3的时候,source2准备为0,开始执行:此时:source2先执行,然后执行source1;所以:先执行:3 0;然后马上执行4 0;(source2变为0 的时候,source还没跳到4,所以为3;source2为0后,source1马上要跳到4,所以会在执行一个4 0;)
然后按秒执行:5 0; 6 0; 7 0; 8 0;
此时:source1和source2又同时要变,还是source2优先,此时:source1为8,所以:8 1;然后马上9 1;
最终就是这种循环:两个同时执行,等N个;然后在同时执行两个,在等N个;
zip
如果zip使用combineLatest的第一个例子,那么结果为:
// [0,0,0]
// [1,1,1]
// [2,2,2]
// [3,3,3]
// ......
zip会自动同步N个流。(会等待每个流完成后,再执行下一个)。
zip的特点是只会取几个observable对应的index的值进行计算,例子如下:
const source1 = interval(1000).pipe(take(20));
const source2 = interval(5000).pipe(take(20));
const source3 = of(4, 5);
const example = source1.pipe(zip(source2, (x, y) => {
console.log(x, y); // x:是source1的index;y:是source2的index;
// 0 0; 1 1; 2 2; 3 3; 4 5; 5 5; .......
return x + y
}));
example.subscribe({
next: value => {
// console.log(value);
},
error: err => {
console.log("Error: " + err);
},
complete: () => {
console.log("complete");
}
});
异步通信,zip和combineLatest,在功能上基本相同,都是将两个以及两个以上的流数据合并成一个流返回。
zip和combineLatest的区别在于,zip是在将要合一个并的两个流都有数据返回时合并后返回,而combineLatest是只有有一个流被抛出来,无论另外一个流是否更新,都会合并返回。
concatMap
concatMap就是map加上concatAll。
合并前:
let source = fromEvent(document.body, 'click');
let example = source.pipe(
map(e => interval(1000).pipe(take(3))),
concatAll()
)
example.subscribe(res => {
console.log(res);
})
合并后:
let source = fromEvent(document.body, 'click');
let example = source.pipe(
concatMap(() => interval(1000).pipe(take(3)))
)
example.subscribe( res=>{
console.log(res); // concat一样,按点击的次数N,依次执行N次:0 1 2。(就是concat的效果)
});
mergeMap
mergeMap同样是mergeAll加上map。
同时会触发click事件,谁先返回,就先执行;类似merge效果。
合并前:
let source = fromEvent(document.body, 'click');
let example = source.pipe(
map(e => interval(1000).pipe(take(3))),
mergeAll()
)
example.subscribe(res => {
console.log(res);
})
合并后:
let source = fromEvent(document.body, 'click');
let example = source.pipe(
mergeMap(() => interval(1000).pipe(take(3)))
)
example.subscribe( res=>{
console.log(res)
});
switchMap
switch在rxjs6中只有switchMap
switch对比merge和concat有个特点就是附属observable发起订阅后会立刻解绑主observable。
let source = fromEvent(document.body, 'click');
let example = source.pipe(
switchMap(() => interval(2000).pipe(take(3)))
)
example.subscribe( res=>{
console.log(res)
});
注:之前绑定的事件接触,只执行当前click绑定的事件。
三个map都有第二个参数,一个回调函数,函数用来处理每个observable发起订阅后的回调操作,函数的参数有四个,分别是:
- 外部 observable 送出的元素
- 內部 observable 送出的元素
- 外部 observable 送出元素的 index
- 內部 observable 送出元素的 index
拿concatMap举例,在附属observable发起订阅后可以通过回调函数拿到observable的发送值进行操作,类似的应用场景在平常有很多。
function getPostData() {
return fetch('https://jsonplaceholder.typicode.com/posts/1')
.then(res => res.json())
}
var source = fromEvent(document.body, 'click');
var example = source.pipe(
concatMap(
e => from(getPostData()),
(e, res, eIndex, resIndex) => res.title)
)
example.subscribe({
next: (value) => {
console.log(value);
},
error: (err) => {
console.log('Error: ' + err);
},
complete: () => {
console.log('complete');
}
});
Observable是可以被多次订阅的
例如下边这个例子,sourcebe被订阅了两次。
let source = interval(1000).pipe(take(3));
let observerA = {
next: value => console.log('A next: ' + value),
error: error => console.log('A error: ' + error),
complete: () => console.log('A complete!')
}
let observerB = {
next: value => console.log('B next: ' + value),
error: error => console.log('B error: ' + error),
complete: () => console.log('B complete!')
}
source.subscribe(observerA);
setTimeout(() => {
source.subscribe(observerB);
}, 1000);
// 结果:结果 有8个:
// "A next: 0"
// "A next: 1"
// "B next: 0"
// "A next: 2"
// "A complete!"
// "B next: 1"
// "B next: 2"
// "B complete!"
但是这种重复订阅有个问题就是,各个订阅都是独立的,有些时候我门希望新的订阅是接在上个订阅之后的(数据没有共享),这个时候这种方式就不能满足需求了,使用subject就可以完成这种需要:
let source = interval(1000).pipe(take(3));
let observerA = {
next: value => console.log('A next: ' + value),
error: error => console.log('A error: ' + error),
complete: () => console.log('A complete!')
}
let observerB = {
next: value => console.log('B next: ' + value),
error: error => console.log('B error: ' + error),
complete: () => console.log('B complete!')
}
let subject = new Subject()
source.subscribe(subject);
subject.subscribe(observerA)
setTimeout(() => {
subject.subscribe(observerB);
}, 1000);
// 结果:只有7个:
// "A next: 0"
// "A next: 1"
// "B next: 1"
// "A next: 2"
// "B next: 2"
// "A complete!"
// "B complete!"
如果改成5s:
let source = interval(1000).pipe(take(3));
let observerA = {
next: value => console.log('A next: ' + value),
error: error => console.log('A error: ' + error),
complete: () => console.log('A complete!')
}
let observerB = {
next: value => console.log('B next: ' + value),
error: error => console.log('B error: ' + error),
complete: () => console.log('B complete!')
}
let subject = new Subject()
source.subscribe(subject);
subject.subscribe(observerA)
setTimeout(() => {
subject.subscribe(observerB);
}, 5000);
// "A next: 0"
// "A next: 1"
// "A next: 2"
// "A complete!"
// "B complete!"
BehaviorSubject
每次有新订阅的时候都会发送给它当前的最新值.
var subject = new BehaviorSubject(0); // 0 起始值
var observerA = {
next: value => console.log('A next: ' + value),
error: error => console.log('A error: ' + error),
complete: () => console.log('A complete!')
}
var observerB = {
next: value => console.log('B next: ' + value),
error: error => console.log('B error: ' + error),
complete: () => console.log('B complete!')
}
subject.subscribe(observerA);
// "A next: 0"
subject.next(1);
// "A next: 1"
subject.next(2);
// "A next: 2"
subject.next(3);
// "A next: 3"
setTimeout(() => {
subject.subscribe(observerB);
// "B next: 3"
},3000)
Subject和BehaviorSubject的区别:
pluck
pluck('subtree'),
concatAll的使用
concatMap
+from
:
of([1, 2, 3, 4]).pipe(
concatMap(res => from(res)),
tap(res => {
console.log('from:', res); // 1 2 3 4
})
).subscribe(res => {
console.log('subscribe:', res); // 1 2 3 4
})
数组拆分成每一项,然后每一项都是一个流。
增加filter
:
let list = [
{id: 1, name: '张三', age: 111},
{id: 2, name: '李四', age: 222},
{id: 3, name: '王五', age: 333},
]
of(list)
.pipe(
concatMap(res => from(res)),
tap(res => {
console.log('from:', res); // 1 2 3 都会执行
}),
filter(res => res.id === 2),
).subscribe(res => {
console.log('subscribe:', res); // 只返回2:{id: 2, name: "李四", age: 222}
})
增加pluck
:
let list = [
{id: 1, name: '张三', age: 111},
{id: 2, name: '李四', age: 222},
{id: 3, name: '王五', age: 333},
]
of(list)
.pipe(
concatMap(res => from(res)),
tap(res => {
console.log('from:', res); // 1 2 3 都会执行
}),
filter(res => res.id === 2),
pluck('name')
).subscribe(res => {
console.log('subscribe:', res); // 只返回2的name:李四
})
scan
-如果多个符合条件的,可以再合并在一起:
参考自己的:https://blog.csdn.net/weixin_42995876/article/details/100081635
let list = [
{id: 1, name: '张三', age: 111},
{id: 2, name: '李四', age: 222},
{id: 3, name: '王五', age: 333},
{id: 4, name: '赵六', age: 444},
{id: 5, name: '钱七', age: 555},
]
of(list)
.pipe(
concatMap(res => from(res)),
tap(res => {
console.log('from:', res); // 1 2 3 4 5 都会执行
}),
filter(res => res.id === 2 || res.id === 3 || res.id === 4),
scan((prev: any, next) => [...prev, next], []), // 类似arr.reduce
).subscribe(res => {
console.log('subscribe:', res); // 会执行3次,但是最后一次是我们预期的结果:2,3,4.
}
)