RxJS速成 (下)
上一部分: http://www.cnblogs.com/cgzl/p/8641738.html
Subject
Subject比较特殊, 它即是Observable又是Observer.
作为Observable, Subject是比较特殊的, 它可以对多个Observer进行广播, 而普通的Observable只能单播, 它有点像EventEmitters(事件发射器), 维护着多个注册的Listeners.
作为Observable, 你可以去订阅它, 提供一个Observer就会正常的收到推送的值. 从Observer的角度是无法分辨出这个Observable是单播的还是一个Subject.
从Subject内部来讲, subscribe动作并没有调用一个新的执行来传递值, 它只是把Observer注册到一个列表里, 就像其他库的AddListener一样.
作为Observer, 它是一个拥有next(), error(), complete()方法的对象, 调用next(value)就会为Subject提供一个新的值, 然后就会多播到注册到这个Subject的Observers.
例子 subject.ts:
import { Subject } from "rxjs/Subject"; const subject = new Subject(); const subscriber1 = subject.subscribe({
next: (v) => console.log(`observer1: ${v}`)
});
const subscriber2 = subject.subscribe({
next: (v) => console.log(`observer2: ${v}`)
}); subject.next(1);
subscriber2.unsubscribe();
subject.next(2); const subscriber3 = subject.subscribe({
next: (v) => console.log(`observer3: ${v}`)
}); subject.next(3);
订阅者1,2从开始就订阅了subject. 然后subject推送值1的时候, 它们都收到了.
然后订阅者2, 取消了订阅, 随后subject推送值2, 只有订阅者1收到了.
后来订阅者3也订阅了subject, 然后subject推送了3, 订阅者1,3都收到了这个值.
下面是一个angular 5的例子:
app.component.html:
<h3>从Subject共享Observable到多个Subscribers</h3>
<input type="text" placeholder="start typing" (input)="mySubject.next($event)" (keyup)="mySubject.next($event)"> <br> Subscriber to input events got {{inputValue}}
<br>
<br> Subscriber to keyup events got {{keyValue}}
app.component.ts:
import { Component } from '@angular/core';
import { Subject } from 'rxjs/Subject';
import 'rxjs/add/operator/filter';
import 'rxjs/add/operator/map'; @Component({
selector: 'app-root',
templateUrl: './app.component.html',
styleUrls: ['./app.component.css']
})
export class AppComponent {
title = 'app'; keyValue: string;
inputValue: string; mySubject: Subject<Event> = new Subject(); constructor() {
// subscriber 1
this.mySubject.filter(({ type }) => type === 'keyup')
.map(e => (<KeyboardEvent>e).key)
.subscribe(value => this.keyValue = value); // subscriber 2
this.mySubject.filter(({ type }) => type === 'input')
.map(e => (<HTMLInputElement>e.target).value)
.subscribe(value => this.inputValue = value);
}
}
input和keyup动作都把event推送到mySubject, 然后mySubject把值推送给订阅者, 订阅者1通过过滤和映射它只处理keyup类型的事件, 而订阅者2只处理input事件.
效果:
BehaviorSubject
BehaviorSubject 是Subject的一个变种, 它有一个当前值的概念, 它会把它上一次发送给订阅者值保存起来, 一旦有新的Observer进行了订阅, 那这个Observer马上就会从BehaviorSubject收到这个当前值.
也可以这样理解BehaviorSubject的特点:
- 它代表一个随时间变化的值, 例如, 生日的流就是Subject, 而一个人的年龄流就是BehaviorSubject.
- 每个订阅者都会从BehaviorSubject那里得到它推送出来的初始值和最新的值.
- 用例: 共享app状态.
例子 behavior-subject.ts:
import { BehaviorSubject } from "rxjs/BehaviorSubject"; const subject = new BehaviorSubject(0); subject.subscribe({
next: v => console.log(`Observer1: ${v}`)
}); subject.next(1);
subject.next(2); subject.subscribe({
next: v => console.log(`Observer2: ${v}`)
}); subject.next(3);
效果:
常用Operators:
concat
concat: 按顺序合并observables. 只会在前一个observable结束之后才会订阅下一个observable.
它适合用于顺序处理, 例如http请求.
例子:
import { Observable } from "rxjs/Observable";
import 'rxjs/add/observable/timer';
import 'rxjs/add/operator/mapTo';
import 'rxjs/add/observable/concat'; let firstReq = Observable.timer(3000).mapTo('First Response');
let secondReq = Observable.timer(1000).mapTo('Second Response'); Observable.concat(firstReq, secondReq)
.subscribe(res => console.log(res));
效果:
merge
把多个输入的observable交错的混合成一个observable, 不按顺序.
merge实际上是订阅了每个输入的observable, 它只是把输入的observable的值不带任何转换的发送给输出的Observable. 只有当所有输入的observable都结束了, 输出的observable才会结束. 任何在输入observable传递来的错误都会立即发射到输出的observable, 也就是把整个流都杀死了 .
例子:
import { Observable } from "rxjs/Observable";
import 'rxjs/add/observable/timer';
import 'rxjs/add/operator/mapTo';
import 'rxjs/add/observable/merge'; let firstReq = Observable.timer(3000).mapTo('First Response');
let secondReq = Observable.timer(1000).mapTo('Second Response'); Observable.merge(firstReq, secondReq)
.subscribe(res => console.log(res));
效果:
mergeMap (原来叫flatMap)
mergeMap把每个输入的Observable的值映射成Observable, 然后把它们混合成一个Observable.
mergeMap可以把嵌套的observables拼合成非嵌套的observable.
它有这些好处:
- 不必编写嵌套的subscribe()
- 把每个observable发出来的值转换成另一个observable
- 自动订阅内部的observable并且把它们(可能)交错的合成一排.
这个还是通过例子来理解比较好:
import { Observable } from "rxjs/Observable";
import 'rxjs/add/observable/from';
import 'rxjs/add/operator/mergeMap'; function getData() {
const students = Observable.from([
{ name: 'Dave', age: 17 },
{ name: 'Nick', age: 18 },
{ name: 'Lee', age: 15 }
]); const teachers = Observable.from([
{ name: 'Miss Wan', age: 28 },
{ name: 'Mrs Wang', age: 31 },
]); return Observable.create(
observer => {
observer.next(students);
observer.next(teachers);
}
);
} getData()
.mergeMap(persons => persons)
.subscribe(
p => console.log(`Subscriber got ${p.name} - ${p.age}`)
);
效果:
switchMap
switchMap把每个值都映射成Observable, 然后使用switch把这些内部的Observables合并成一个.
switchMap有一部分很想mergeMap, 但也仅仅是一部分像而已.
因为它还具有取消的效果, 每次发射的时候, 前一个内部的observable会被取消, 下一个observable会被订阅. 可以把这个理解为切换到一个新的observable上了.
这个还是看marble图比较好理解:
例子:
// 立即发出值, 然后每5秒发出值
const source = Rx.Observable.timer(0, 5000);
// 当 source 发出值时切换到新的内部 observable,发出新的内部 observable 所发出的值
const example = source.switchMap(() => Rx.Observable.interval(500));
// 输出: 0,1,2,3,4,5,6,7,8,9...0,1,2,3,4,5,6,7,8
const subscribe = example.subscribe(val => console.log(val));
更好的例子是: 网速比较慢的时候, 客户端发送了多次重复的请求, 如果前一次请求在2秒内没有返回的话, 那么就取消前一次请求, 不再需要前一次请求的结果了, 这里就应该使用debounceTime配合switchMap.
mergeMap vs switchMap的例子
mergeMap:
import { Observable } from "rxjs/Observable";
import 'rxjs/add/observable/interval';
import 'rxjs/add/operator/take';
import 'rxjs/add/operator/map';
import 'rxjs/add/operator/mergeMap';
import 'rxjs/add/operator/switchMap'; const outer = Observable.interval(1000).take(2); const combined = outer.mergeMap(x => {
return Observable.interval(400)
.take(3)
.map(y => `outer ${x}: inner ${y}`);
}); combined.subscribe(res => console.log(`result ${res}`));
效果:
switchMap:
import { Observable } from "rxjs/Observable";
import 'rxjs/add/observable/interval';
import 'rxjs/add/operator/take';
import 'rxjs/add/operator/map';
import 'rxjs/add/operator/mergeMap';
import 'rxjs/add/operator/switchMap'; const outer = Observable.interval(1000).take(2); const combined = outer.switchMap(x => {
return Observable.interval(400)
.take(3)
.map(y => `outer ${x}: inner ${y}`);
}); combined.subscribe(res => console.log(`result ${res}`));
zip
zip操作符也会合并多个输入的observables成为一个observable. 多个输入的observable的值, 按顺序, 按索引进行合并, 如果某一个observable在该索引上的值还没有发射值, 那么会等它, 直到所有的输入observables在该索引位置上的值都发射出来, 输出的observable才会发射该索引的值.
例子:
import { Observable } from "rxjs/Observable";
import 'rxjs/add/observable/of';
import 'rxjs/add/observable/zip'; let age$ = Observable.of<number>(27, 25, 29);
let name$ = Observable.of<string>('Foo', 'Bar', 'Beer');
let isDev$ = Observable.of<boolean>(true, true, false); Observable
.zip(age$,
name$,
isDev$,
(age: number, name: string, isDev: boolean) => ({ age, name, isDev }))
.subscribe(x => console.log(x));
效果:
就不往下写了, 其实看文档就行, 最重要的还是上一部分.
RxJS速成 (下)的更多相关文章
- RxJS速成 (上)
What is RxJS? RxJS是ReactiveX编程理念的JavaScript版本.ReactiveX是一种针对异步数据流的编程.简单来说,它将一切数据,包括HTTP请求,DOM事件或者普通数 ...
- RxJS 中的创建操作符
RxJs 中创建操作符是创建数据流的起点,这些操作符可以凭空创建一个流或者是根据其它数据形式创建一个流. Observable的构造函数可以直接创建一个数据流,比如: const $source=ne ...
- C++程序结构---1
C++ 基础教程Beta 版 原作:Juan Soulié 翻译:Jing Xu (aqua) 英文原版 本教程根据Juan Soulie的英文版C++教程翻译并改编. 本版为最新校对版,尚未定稿.如 ...
- 一个简单的html5页面在线速成工具!(当然本文主要说下他的成果的结构)
分享一个好玩的web app页面速成工具 当然主要是让大家看下他的原理 看着他的结构大家就该猜到这个了.这个是利用换页之后给当前div加了一个active,然后利用css控制效果 这个毫无疑问是采用最 ...
- wcf系列学习5天速成——第三天 分布性事务的使用 有时间再验证下 不同库的操作
原文地址:http://www.cnblogs.com/huangxincheng/archive/2011/11/06/2238273.html 今天是速成的第三天,再分享一下WCF中比较常用的一种 ...
- 【Spark机器学习速成宝典】基础篇01Windows下spark开发环境搭建+sbt+idea(Scala版)
注意: spark用2.1.1 scala用2.11.11 材料准备 spark安装包 JDK 8 IDEA开发工具 scala 2.11.8 (注:spark2.1.0环境于scala2.11环境开 ...
- 2流高手速成记(之八):基于Sentinel实现微服务体系下的限流与熔断
我们接上回 上一篇中,我们进行了简要的微服务实现,也体会到了SpringCloudAlibaba的强大和神奇之处 我们仅改动了两个注释,其他全篇代码不变,原来的独立服务就被我们分为了provider和 ...
- RxJS + Redux + React = Amazing!(译一)
今天,我将Youtube上的<RxJS + Redux + React = Amazing!>翻译(+机译)了下来,以供国内的同学学习,英文听力好的同学可以直接看原版视频: https:/ ...
- RxJS + Redux + React = Amazing!(译二)
今天,我将Youtube上的<RxJS + Redux + React = Amazing!>的后半部分翻译(+机译)了下来,以供国内的同学学习,英文听力好的同学可以直接看原版视频: ht ...
随机推荐
- Json序列化、反序列化
引用 using Newtonsoft.Json; using Newtonsoft.Json.Converters; 把Json字符串反序列化为对象 1.目标对象 = JavaScriptConve ...
- android WebP解析开源库-支持高清无损
在我们的项目中需要支持WebP高清无损图片,推荐一个我们已经使用的解析开源库给大家:https://github.com/keshuangjie/WebpExample/tree/master/lib ...
- 致IT同仁 — IT人士常犯的17个职场错误
记得刚参加工作时(那是97年),中国的IT刚刚兴起,那时,作为一个IT人士是一件很光荣的事,而那时的我正在做电气和电子相关的工作.99年第一次跳槽,进入了IT行业做软件开发.至今,中国的IT已经走过了 ...
- Flex下拉框
<?xml version="1.0" encoding="utf-8"?> <s:Application xmlns:fx="ht ...
- window.load 和$(document).ready() 区别
1.执行时间 window.onload必须等到页面内包括图片的所有元素加载完毕后才能执行. $(document).ready()是DOM结构绘制完毕后就执行,不必等到加载完毕.2.编写个数不同 w ...
- Stanford CS224N 第二课: word2vec详细介绍
word2vec模型 word2vec 模型有两种: Continuous Skip-gram Model 和 Continuous Bag of Words Model (CBOW). 本文主要讲解 ...
- Spring的Bean有哪些作用域?
Spring的Bean有以下五种作用域: 1.singleton:SpringIOC容器只会创建该Bean的唯一实例: 2.prototype:每次请求都创建一个实例: 3.requset:每次HTT ...
- Bzoj3930: [CQOI 2015] 选数 & COGS2699: [CQOI 2015] 选数加强版
题面 Bzoj COGS加强版 Sol 非加强版可以枚举AC这里不再讲述 设\(f(i)\)表示在\([L, H]\)取\(N\)个,\(gcd为i\)的方案数 \(F(i)=\sum_{i|d}f( ...
- luogu1402 酒店之王
题目描述 XX酒店的老板想成为酒店之王,本着这种希望,第一步要将酒店变得人性化.由于很多来住店的旅客有自己喜好的房间色调.阳光等,也有自己所爱的菜,但是该酒店只有p间房间,一天只有固定的q道不同的菜. ...
- mycat操作MySQL第一篇:全局表
1.安装mycat,点击bin下面startup_nowrap.bat启动 2.客户端连接mycat:server.xml里面的 <!--连接mycat用户名和密码.数据库--> < ...