目录
实例练习:创建并订阅一个可观察对象,让它的观察者把接收的消息,记录到控制台中
创建可观察对象(法二:订阅者函数 sequenceSubscriber)
Observable 基本概念
- Observable(可观察对象),属于 RxJS库 里的 对象,用来处理 异步事件,例如 HTTP 请求
- 在 Angular 中,所有的 HTTP 请求 返回的都是 Observable
- promise 和 Observable 本质上相同:都是生产者主动向消费者 “push” 产品,而消费者是被动接收
- 两者区别:Observable 可以发送任意多值,且被订阅之前不会执行
- Observable 用于在发送方和接收方之间传输消息,可以将这些消息看成是 ”流“
- 创建 Observable 对象时,需要传入 订阅者函数(subscriber)作为构造函数的参数,订阅者函数也就是生产者向消费者推送消息的地方
- 被消费者subscribe(订阅)之前,订阅者函数不会被执行,直到 subscribe() 函数 被调用,表示 订阅
- subscribe() 函数接收 observer(观察者)对象 作为参数,观察者 用于 观察可观察对象(发送方)
- subscribe() 函数返回 subscription 对象,里面有 unsubscribe() 函数,用于消费者随时 取消订阅
- 消息的发送可以是同步的,也可以是异步的
- 可以使用 RxJS 操作符,在这些消息被接收方接收之前,对它们进行一系列的 处理、转换
observer 的三个属性
- 推荐把 Observable 用于:事件处理、异步编程、处理多个值
- observer(观察者)是对象,包含三个属性:next,error,complete
- 【必须】next:以接收的值作为入参,正常时执行
- 【可选】error:出错时执行
- 【可选】complete:传输完成时执行
创建可观察对象(法一:of()、from())
- of(...items):返回 Observable 实例,用 同步方式,把 参数中的值 发送出来
- from(iterable) :返回 Observable 实例,用于把 参数中的数组 转换成一个(发送 多个值 的)可观察对象
实例练习:创建并订阅一个可观察对象,让它的观察者把接收的消息,记录到控制台中
- 通过 of()、form() 接受值或者数组,创建可观察对象
- 创建 可观察对象的参数 观察者,并定义观察者的 next()、error()、complete() 方法
- 将 观察者 放入 可观察对象 中,并进行订阅
- 简化上面三步:创建可观察对象,将观察者的方法直接写入订阅方法中
// 创建可观察对象(通过 of()): const myObservable = of(1, 2, 3); // 观察者: const myObserver = { next: x => console.log('Observer got a next value: ' + x), error: err => console.error('Observer got an error: ' + err), complete: () => console.log('Observer got a complete notification'), }; // 可观察对象被订阅: myObservable.subscribe(myObserver); —————————————————————————————————————————————————————————————————————————— // 简写 可观察对象被订阅: myObservable.subscribe( x => console.log('Observer got a next value: ' + x), err => console.error('Observer got an error: ' + err), () => console.log('Observer got a complete notification') ); —————————————————————————————————————————————————————————————————————————— // Logs: // Observer got a next value: 1 // Observer got a next value: 2 // Observer got a next value: 3 // Observer got a complete notification
创建可观察对象(法二:订阅者函数 sequenceSubscriber)
- 通过 订阅者函数 sequenceSubscriber() 接收观察者,并在其中定义 next()、error()、complete() 方法
- 显示创建可观察对象,并传入 订阅者函数 new Observable(sequenceSubscriber),最后存储在一个变量里
- 将上述变量进行订阅,订阅方法内再次调用 next()、error()、complete() 方法
// 订阅者函数:生产者向消费者推送消息的地方 function sequenceSubscriber(observer) { observer.next(1); observer.next(2); observer.next(3); observer.complete(); return {unsubscribe() {}}; } // 创建 Observable 对象时,需要传入订阅者函数(subscriber)作为构造函数的参数 const sequence = new Observable(sequenceSubscriber); // Observable 对象被订阅 sequence.subscribe({ next(num) { console.log(num); }, complete() { console.log('Finished sequence'); } }); // Logs: // 1 // 2 // 3 // Finished sequence
- 加强上述过程,用 内联方法 定义 订阅者函数 (这没看懂):
function fromEvent(target, eventName) { return new Observable((observer) => { const handler = (e) => observer.next(e); // Add the event handler to the target target.addEventListener(eventName, handler); return () => { // Detach the event handler from the target target.removeEventListener(eventName, handler); }; }); } // 用这个函数来创建可发布 keydown 事件的可观察对象: const ESC_KEY = 27; const nameInput = document.getElementById('name') as HTMLInputElement; const subscription = fromEvent(nameInput, 'keydown') .subscribe((e: KeyboardEvent) => { if (e.keyCode === ESC_KEY) { nameInput.value = ''; } });
Observable 捕获错误
- 由于可观察对象会 异步 生成值,无法用 try / catch 捕获错误
- 在观察者中指定 error() 回调处理错误
myObservable.subscribe({ next(num) { console.log('Next num: ' + num)}, error(err) { console.log('Received an errror: ' + err)} });
可观察对象 是 Angular 中处理 异步操作 的接口
- 可观察对象 是 Angular 中处理 异步操作 的接口,作用如下:
- EventEmitter 类派生自 Observable
- HTTP 模块使用 Observable 来处理 AJAX 请求和响应
- 路由器、表单模块使用 Observable 监听对用户输入事件的响应
在组件之间传递数据
- EventEmitter 类,通过组件的 @Output() 装饰器 发送一些值
- EventEmitter 类,添加了 emit() 方法,这样它就可以发送任意值了
- 调用 emit(),就会把所发送的值传给观察者的 next() 方法
- 下面这个范例组件监听了 open 和 close 事件:<zippy (open)="onOpen($event)" (close)="onClose($event)"></zippy>
<zippy (open)="onOpen($event)" (close)="onClose($event)"></zippy> // 组件的定义如下: @Component({ selector: 'zippy', template: ` <div class="zippy"> <div (click)="toggle()">Toggle</div> <div [hidden]="!visible"> 我是内容 </div> </div>`}) export class ZippyComponent { // 当前状态可见 visible = true; // EventEmitter 类,通过组件的 @Output() 装饰器 发送一些值 @Output() open = new EventEmitter<any>(); @Output() close = new EventEmitter<any>(); toggle() { // 获取当前状态并进行取反 this.visible = !this.visible; if (this.visible) { this.open.emit(null); } else { this.close.emit(null); }}}
![]()
基于可观察对象(Observable)的 HTTP API
- Angular 的 HttpClient 从 HTTP 方法调用中,返回了可观察对象 Observable
- 例如:http.get(‘/api’) 就会返回可观察对象 Observable
- 相对于基于承诺(Promise)的 HTTP API,基于可观察对象(Observable)的 HTTP API 有一系列 优点:
- 可观察对象不会修改服务器的响应(和在承诺上串联起来的 .then() 调用一样),但可以使用一系列 操作符 来 按需转换 这些响应
- 可观察对象的 HTTP 请求可以通过 unsubscribe() 【翻译:取消订阅】方法取消
- 请求可以进行配置,可以获取进度事件的变化
- 失败的请求很容易重试
Async 管道
- AsyncPipe 会订阅 可观察对象 或 承诺,并 返回 其发出的 最后一个值
- 当发出新值时,Async 管道就会把该组件标记为 需要进行变更检查的(故可能导致刷新)
- 下面的例子把 time 这个可观察对象绑定到了组件的视图中。它会不断使用 当前时间 更新组件的视图
@Component({
selector: 'async-observable-pipe',
template: `<div><code>observable|async</code>:// 下面就是 Async 管道
Time: {{ time | async }}</div>`
})
export class AsyncObservablePipeComponent {
time = new Observable<string>(observer => {
setInterval(() => observer.next(new Date().toString()), 1000);
});
}
- Observable:可观察对象
- observer:观察者
- observer.next():观察者下一步操作
路由器 (router)【存在问题】
- Router.events 以 Observable 的形式提供了其事件,可以使用 RxJS 中的 filter()操作符 来找到并订阅事件
- NavigationStart:
import { Router, NavigationStart } from '@angular/router'; import { filter } from 'rxjs/operators'; export class Routable1Component implements OnInit { navStart: Observable<NavigationStart>; constructor(private router: Router) { this.navStart = router.events.pipe( // 过滤得到指定类型的事件 filter(evt => evt instanceof NavigationStart) ) as Observable<NavigationStart>; } ngOnInit() { // 进行订阅 this.navStart.subscribe(evt => console.log('Navigation Started!')); } }
![]()
- ActivatedRoute:
import { ActivatedRoute } from '@angular/router'; export class Routable2Component implements OnInit { constructor(private activatedRoute: ActivatedRoute) {} ngOnInit() { // 进行订阅 this.activatedRoute.url .subscribe(url => console.log('即将跳转的路由地址是: ' + url)); } }
![]()
响应式表单 (reactive forms)
- FormControl 的 valueChanges 属性 和 statusChanges 属性 包含了 会发出 变更事件 的可观察对象
import { FormGroup } from '@angular/forms'; export class MyComponent implements OnInit { // 名字更改日志 nameChangeLog: string[] = []; // 响应式表单 heroForm: FormGroup; ngOnInit() { this.logNameChange(); } logNameChange() { // 获取名字响应式表单 const nameControl = this.heroForm.get('name'); // 名字响应式表单的 valueChanges属性 包含变更事件 nameControl.valueChanges.forEach( (value: string) => this.nameChangeLog.push(value) );} }
![]()
Observables VS promises
- 可观察对象在被订阅之前,不会执行;承诺是在创建时立即执行
- 可观察对象能获取多个值,承诺只获取一个
- 可观察对象会区分 串联处理 和 订阅语句,承诺只有 .then() 语句
- 可观察对象的 subscribe() 会处理错误,承诺会把错误推送给子承诺
创建 转换 订阅 取消订阅 Observables new Observable((observer) => {
observer.next(123);
});obs.pipe(map((value) => value * 2)); sub = obs.subscribe((value) => {
console.log(value)
});sub.unsubscribe(); promises new Promise((resolve, reject) => {
resolve(123);
});promise.then((value) => value * 2); promise.then((value) => {
console.log(value);
});承诺被解析时隐式完成
Observables VS Events API
创建与取消 订阅 配置 Observables // 创建可观察对象订阅
let clicks$ = fromEvent(buttonEl, ‘click’);
let subscription = clicks$.subscribe(e => console.log(‘Clicked’, e))// 取消订阅
subscription.unsubscribe();observable.subscribe(() => { }); // 监听按键,提供一个流来表示这些输入的值
fromEvent(inputEl, 'keydown').pipe(
map(e => e.target.value)
);Events API // 建立并开始监听事件
button.addEventListener(‘click’, handler);
// 取消监听
button.removeEventListener(‘click’, handler);function handler(e) {
console.log(‘Clicked’, e);
}element.addEventListener(eventName, (event) => { }); // 不支持配置
element.addEventListener(eventName, (event) => { });
Observables VS Array
- 可观察对象会 随时间生成值,数组是 静态创建值
- 可观察对象是 异步 的,数组是 同步 的
- 假设符号 ➞ 表示异步传值:
给出值 concat() filter():全部找到才停止 find():找到一个就停止 findIndex()【这没太看懂】 遍历:tap()、forEach() map() reduce()【这没太看懂】 Observables obs: ➞1➞2➞3➞5➞7
obsB: ➞'a'➞'b'➞'c'concat(obs, obsB)
➞1➞2➞3➞5➞7➞'a'➞'b'➞'c'obs.pipe(filter((v) => v>3))
➞5➞7obs.pipe(find((v) => v>3))
➞5obs.pipe(findIndex((v) => v>3))
➞3obs.pipe(tap((v) => {
console.log(v);
}))
1, 2, 3, 5, 7obs.pipe(map((v) => -v))
➞-1➞-2➞-3➞-5➞-7obs.pipe(reduce((s,v)=> s+v, 0))
➞18Array arr: [1, 2, 3, 5, 7]
arrB: ['a', 'b', 'c']arr.concat(arrB)
[1,2,3,5,7,'a','b','c']arr.filter((v) => v>3)
[5, 7]arr.find((v) => v>3)
5arr.findIndex((v) => v>3)
3arr.forEach((v) => {
console.log(v);
})
1, 2, 3, 5, 7arr.map((v) => -v)
[-1, -2, -3, -5, -7]arr.reduce((s,v) => s+v, 0)
18