rxjs是什么
它是一种针对异步数据流的编程。简单来说,它将一切数据,包括HTTP请求,DOM事件或者普通数据等包装成流的形式,然后用强大丰富的操作符对流进行处理,使你能以同步编程的方式处理异步数据,并组合不同的操作符来轻松优雅的实现你所需要的功能。
RxJS中的流以Observable对象呈现,获取数据需要订阅Observable,形式如下:
const ob = http$.getSomeList(); //getSomeList()返回某个由`Observable`包装后的http请求
ob.subscribe((data) => console.log(data));
//在变量末尾加$表示Observable类型的对象。不要用 import { Observable } from 'rxjs'这种方式导入,这会导入整个rxjs库,按需导入的方式如下:
import { Observable } from 'rxjs/Observable' //导入类
import 'rxjs/add/operator/map' // 导入实例操作符
import 'rxjs/add/observable/forkJoin' // 导入类操作符快速入门
Observable
称为可观察序列,简单来说数据就在Observable中流动,你可以使用各种operator对流,例如:
const ob = Observable.interval(1000);
ob.take(3).map(n => n * 2).filter(n => n > 2);第一步代码我们通过类方法interval创建了一个Observable序列,ob作为源会每隔1000ms发射一个递增的数据,即0 -> 1 -> 2。第二步我们使用操作符对流进行处理,take(3)表示只取源发射的前3个数据,取完第三个后关闭源的发射;map表示将流中的数据进行映射处理,这里我们将数据翻倍;filter表示过滤掉出符合条件的数据,根据上一步map的结果,只有第二和第三个数据会留下来。
上面我们已经使用同步编程创建好了一个流的处理过程,但此时ob作为源并不会立刻发射数据,如果我们在map中打印n是不会得到任何输出的,因为ob作为Observable序列必须被“订阅”才能够触发上述过程,也就是subscribe(发布/订阅模式)。
const ob = Observable.interval(1000);
ob.take(3).map(n => n * 2).filter(n => n > 0).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出错。
Observable.of(1).map(n => n.undefinedMethod()).catch(err => {
// 此处处理catch之前发生的错误
return Observable.of(0); // 返回一个新的序列,该序列成为新的流。
});- Observer
- Operator
创建可观察序列
创建一个序列有很多种方式,我们仅列举常用的几种:
Observable.of(...args)
Observable.of()可以将普通JavaScript数据转为可观察序列Observable.fromPromise(promise)
将Promise转化为ObservableObservable.fromEvent(elment, eventName)
从DOM事件创建序列,例如Observable.fromEvent($input, 'click'),Observable.ajax(url | AjaxRequest)
发送http请求,AjaxRequest参考这里Observable.create(subscribe)这个属于万能的创建方法,一般用于只提供了回调函数的某些功能或者库,在你用这个方法之前先想想能不能用RxJS上的类方法来创建你所需要的序列,
合并序列
也属于创建序列的一种,例如有这样的需求:进入某个页面后拿到了一个列表,然后需要对列表每一项发出一个http请求来获取对应的详细信息,这里我们把每个http请求作为一个序列,然后我们希望合并它们。
合并有很多种方式,例如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('触发一次回调'));实例,实现搜索功能
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)); // 接收数据subject主题
RxJS Subject是一种特殊类型的Observable,允许将值多播到多个观察者Observer。虽然普通的Observable是单播的(每个订阅的Observer都拥有Observable的独立执行),但Subject是多播的。
// 这是实例了一个主题,观察者(可以多个)订阅主题,向主题提供一个值,多个观察者被多播
var subject = new Rx.Subject();
subject.subscribe({next: (v) => console.log('observerA: ' + v)});
subject.subscribe({next: (v) => console.log('observerB: ' + v)});
subject.next(1);
subject.next(2);
输出:
observerA: 1
observerB: 1
observerA: 2
observerB: 2由于Subject是Observer,这也意味着您可以提供Subject作为任何Observable subscribe的参数,
var subject = new Rx.Subject();
subject.subscribe({
next: (v) => console.log('observerA: ' + v)
})
subject.subscribe({
next: (v) => console.log('observerB: ' + v)
})
var observable = Rx.Observable.from([1,2,3]);
observable.subscribe(subject); // You can subscribe providing a Subject
输出:
observerA: 1
observerB: 1
observerA: 2
observerB: 2
observerA: 3
observerB: 3
个人总结的常用操作符:
类操作符(通常为合并序列或从已有数据创建序列)
合并 forkJoin, merge, concat
创建 of, from, fromPromise, fromEvent, ajax, throw实例操作符(对流中的数据进行处理或者控制流程)
map, filter,switchMap, toPromise, catch, take, takeUntil, timeout, debounceTime, distinctUntilChanged, pluck。