RxJS---转载

摘自 此处:https://segmentfault.com/a/1190000012252368

导入方式

避免使用 import { Observable } from 'rxjs'这种方式导入,这会导入整个rxjs库,按需导入的方式如下:
import { Observable } from 'rxjs' //导入类

RxJS团队设计了以下规则来帮助JavaScript开发人员重构import路径:

  • rxjs: 包含创建方法,类型,调度程序和工具库。

    import { Observable, Subject, asapScheduler, pipe, of, from, interval, merge, fromEvent } from 'rxjs';
  • rxjs/operators: 包含所有的管道操作符

    import { map, filter, scan } from 'rxjs/operators';
  • rxjs/webSocket: 包含websocket subject实现.

    import { webSocket } from 'rxjs/webSocket';
  • rxjs/ajax: 包含Rx ajax实现.

    import { ajax } from 'rxjs/ajax';
  • rxjs/testing: 包含RxJS的测试工具库.

    import { TestScheduler } from 'rxjs/testing';

 

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

创建可观察序列

点击此处查看示例

Observable.of(...args)

Observable.of()可以将普通JavaScript数据转为可观察序列

Observable.fromPromise(promise)

Promise转化为Observable

Observable.fromEvent(elment, eventName)

DOM事件创建序列,例如Observable.fromEvent($input, 'click')

Observable.ajax(url | AjaxRequest)

发送http请求,AjaxRequest

Observable.create(subscribe)

这个属于万能的创建方法,一般用于只提供了回调函数的某些功能或者库,在你用这个方法之前先想想能不能用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('触发一次回调'));

 

使用RxJS实现搜索功能

 

搜索是前端开发中很常见的功能,一般是监听<input />keyup事件,然后将内容发送到后台,并展示后台返回的数据。

var text = document.querySelector('#text');
var inputStream = Rx.Observable.fromEvent(text, 'keyup') //为dom元素绑定'keyup'事件
                    .debounceTime(250) // 防抖动(防止重复的请求)
                    .pluck('target', 'value') // 取值
                    .switchMap(url => Http.get(url)) // 将当前输入流替换为http请求
                    .subscribe(data => render(data)); // 接收数据

 

 

 RxJS能简化你的代码,它将与流有关的内部状态封装在流中,而不需要在流外定义各种变量来以一种上帝视角控制流程。

一些操作符

合并 forkJoin, merge, concat
创建 of, from, fromPromise, fromEvent, ajax, throw
实例操作符(对流中的数据进行处理或者控制流程)
map, filter,switchMap, toPromise, catch, take, takeUntil, timeout, debounceTime, distinctUntilChanged, pluck

Map与switchmap\flatmap

let stream = Observable.interval(1000).take(10);
return stream.map(n => Observable.timer(500).map(() => n));

这里stream会返回一个Observable而不是数字。

如果我想要拿到那些数字,我该怎么办?

let stream = Observable.interval(1000).take(10);
return stream.flatMap(n => Observable.timer(500).map(() => n));

这里使用了flatMap而不是mapflatMap将响应数据“打平”,也就是说把映射后新的Observable转化为了数据流,订阅之后会获得这个新Observable发射的数据,而不是Observable本身。

译者注:flatMap有一个很适用的场景,就是搜索框。在用户输入一串字符后,将其发送到服务器并获取搜索结果,这里就涉及到两个Observable

Observable
.fromEvent($input, 'keyup')
.flatMap(text => getHttpResponse(text))
.subscribe(data => console.log(data))

使用flatMap就可以直接获取到新的Observable返回的数据。但是这里存在一个问题,如果用户有多次输入,由于网络原因可能会发生前一次响应时间比后一次长的情况,这时后一次的结果就被覆盖了。
flatMapLatest可以解决这个问题。如果之前的Observable还没有未触发,而又收到了新的ObservableflatMapLatest会取消之前的Observable,只处理最新收到的Observable,这样就保证了处理请求的先后顺序,flatMapLatestRxJS 5.x中已更名为switchMap

forkJoin, zip, combineLatest之间的区别

 

forkJoin

forkJoin合并的流,会在每个被合并的流都发出结束信号时发射一次也是唯一一次数据。假设我们有两个流:

1 const ob1 = Rx.Observable.interval(1000).map(d => `ob1:${d}`).take(3);
2 const ob2 = Rx.Observable.interval(2000).map(d => `ob2:${d}`).take(2);
3 
4 Rx.Observable.forkJoin(ob1, ob2).subscribe((data) => console.log(data));
5 // ["ob1:2", "ob2:1"]

ob1会在发射完第三个数据时停止发射,ob2会在发射完第二个数据时停止,而forkJoin合并后的流会等到ob1ob2都结束时,发射一次数据,也就是触发一次subscribe里的回调,接收到的数据为ob1ob2发射的最后一次数据的数组。

zip

zip工作原理如下,当每个传入zip的流都发射完毕第一次数据时,zip将这些数据合并为数组并发射出去;当这些流都发射完第二次数据时,zip再次将它们合并为数组并发射。以此类推直到其中某个流发出结束信号,整个被合并后的流结束,不再发射数据。

const ob1 = Rx.Observable.interval(1000).map(d => `ob1:${d}`).take(3);
const ob2 = Rx.Observable.interval(2000).map(d => `ob2:${d}`).take(2);

Rx.Observable.zip(ob1, ob2).subscribe({
  next: (data) => console.log(data),
  complete: () => console.log('complete')
});
// ["ob1:0", "ob2:0"] ob1等待ob2发射数据,之后合并
// ["ob1:1", "ob2:1"] 此时ob2结束,整个合并的流也结束
// "complete"

 

 

zipforkJoin的区别在于,forkJoin仅会合并各个子流最后发射的一次数据,触发一次回调;zip会等待每个子流都发射完一次数据然后合并发射,之后继续等待,直到其中某个流结束(因为此时不能使合并的数据包含每个子流的数据)。

combineLatest

combineLatestzip很相似,combineLatest一开始也会等待每个子流都发射完一次数据,但是在合并时,如果子流1在等待其他流发射数据期间又发射了新数据,则使用子流最新发射的数据进行合并,之后每当有某个流发射新数据,不再等待其他流同步发射数据,而是使用其他流之前的最近一次数据进行合并。

const ob1 = Rx.Observable.interval(1000).map(d => `ob1:${d}`).take(3);
const ob2 = Rx.Observable.interval(2000).map(d => `ob2:${d}`).take(2);

Rx.Observable.combineLatest(ob1, ob2).subscribe({
  next: (data) => console.log(data),
  complete: () => console.log('complete')
});
// ["ob1:1", "ob2:0"] ob1等待ob2发射,当ob2发射时ob1已经发射了第二次数据,使用ob1的第二次数据
// ["ob1:2", "ob2:0"] ob1继续发射第三次也是最后一次数据,ob2虽然还未发射,但是可以使用它上一次的数据
// ["ob1:2", "ob2:1"] ob2发射第二次也是最后一次数据,使ob1上一次的数据。
// "complete"

 

 

 

posted @ 2020-10-16 11:06  DurianTRY  阅读(345)  评论(0编辑  收藏  举报