rxjs文档

中文官网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?

RxJSReactiveX编程理念的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表示流结束,不再发射新的数据。
在一个流的生命周期中,errorcomplete只会触发其中一个,可以有多个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请求。
  1. N个请求按顺序串行发出(前一个结束再发下一个)
Observable.concat(...obs).subscribe(detail => console.log('每个请求都触发回调'));
  1. N个请求同时并行发出,对于每一个到达就触发一次回调
Observable.merge(...obs).subscribe(detail => console.log('每个请求都触发回调'));
  1. 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, throwintervaltimer等。
  • 实例操作符(对流中的数据进行处理或者控制流程)
    mapfilterswitchMaptoPromisecatchtaketakeUntiltimeout, debounceTimedistinctUntilChangedpluck

以下参考: https://www.jianshu.com/p/16be96d69143

分类:

创建observable类(上一篇都有)

create,of,from,fromPromise,fromEvent,interval,timer,等

选择器类:

take,first,last,takeLast,

first:取第一个数然后结束,和take(1)效果一样。
takeLasttake用法一样,区别是该方法是取后边几个值,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");
  }
})

lasttakeLast(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的对象(前三个)
})

distinctUntilChangeddistinct不同之处就是,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.
  }
)
  • 1
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值