Angular---rxjs
Rxjs
简介
RxJS是一个主要用于处理异步程序的函数式编程库,可以把 RxJS 想成处理 异步行为 的 Lodash。
常见的异步行为
ajax,定时器,事件
常见的 优化异步回调的方式
Promise,async/await,rxjs
函数式编程
是一种编程思想的,就像面向过程、面向对象一样 比如:
const result = (5 + 6) - 2 * 3;
函数式的写法:
const add = (a, b) => a + b
const mul = (a, b) => a * b
const sub = (a, b) => a - b
const result = sub(add(5, 6), mul(2, 3))
把每个运算包成一个个不同的 function,并用这些 function 组合出我们要的结果,这就是最简单的 Functional Programming。
函数特性
- 一定会有返回值
- 不能产生副作用(纯函数)
- 纯函数的逻辑不能改变数据源
const arr = [1, 2, 3, 4, 5];
arr.slice(0, 3);
console.log(arr); // arr不变,slice就是个纯函数
arr.splice(0, 3, 2);
console.log(arr); // arr改变,splice是不纯的
rxjs vs Promise
- 可观察对象是声明式的,在被订阅之前,它不会开始执行。promise是在创建时就立即执行的。这让可观察对象可用于定义那些应该按需执行的菜谱:
class AppComponent {
newPromise() {
const p = new Promise(resolve => {
console.log('initial a promise'); // 创建后立即触发
});
}
newObservable() {
const o = new Observable(subscriber => {
console.log('initial a newObservable'); // 没有订阅不触发
});
}
}
- 串联 跟第一条类似,只有当调用subscribe方法时,才会执行所有管道函数:
class AppComponent {
// 串联起来会依次执行每个then中的打印日志
newPromise() {
const p = new Promise(resolve => {
resolve(['a', 'b', 'c']);
}).then(res => {
console.log('第一个then');
return res;
}).then(res => {
console.log('第2个then');
return res;
});
}
// 串联起来,但是没有订阅,不会执行
newObservable() {
const o = new Observable(subscriber => {
console.log('initial a newObservable');
subscriber.next(['a', 'b', 'c']);
}).pipe(
map(res => {
console.log('第一个map');
return res;
}),
map(res => {
console.log('第2个map');
return res;
})
); // 不订阅,pipe中的所有函数都不会触发
}
}
- Observable可以手动取消,而Promise被解析时自动完成:
// interval(1000) 每1s触发发送一条数据
const sub = interval(1000).subscribe(res => {
console.log('interval', res);
});
// 可以手动取消订阅(可观察对象订阅为0时,就会停止执行)
class App {
cancelObservable() {
sub.unsubscribe();
}
}
- 错误处理:
// 可观察对象的错误处理工作交给了订阅者的错误处理器,并且发生异常时该订阅者会自动取消对这个可观察对象的订阅
observable.subscribe(() => {
throw new Error('my error');
});
// 承诺会把错误推给其子promise
promise.then(() => {
throw new Error('my error');
});
Observable与Observer
Observable 可观察对象
Observable 可观察对象负责从数据源中推送数据:
import {
Observable } from 'rxjs';
const observable = new Observable(subscriber => {
// 推送三个数据
subscriber.next(1);
subscriber.next(2);
subscriber.next(3);
});
console.log('before subscribe');
observable.subscribe(x => {
console.log('获得 value ' + x);
});
console.log('subscribe');
lazy computations
之前与Promise对比时讲过,只要不订阅(调用subscribe)Observable,Observable的回调函数就不会执行
import {
Observable } from 'rxjs';
const foo = new Observable(subscriber => {
console.log('Hello');
subscriber.next(42);
});
foo.subscribe(x => {
console.log(x);
});
Observable可同步,也可异步 推送值:
import {
Observable } from 'rxjs';
const foo = new Observable(subscriber => {
console.log('Hello');
subscriber.next(42);
});
console.log('before');
foo.subscribe(x => {
console.log(x);
});
console.log('after');
下面是异步:
import {
Observable } from 'rxjs';
const foo = new Observable(subscriber => {
console.log('Hello');
subscriber.next(42);
subscriber.next(100);
subscriber.next(200);
setTimeout(() => {
subscriber.next(300); // happens asynchronously
}, 1000);
});
console.log('before');
foo.subscribe(x => {
console.log(x);
});
console.log('after');
创建Observables
可用new Observable() 创建,但实际情况更多的是用of, from, interval等操作符创建
import {
Observable } from 'rxjs';
const observable = new Observable(function subscribe(subscriber) {
const id = setInterval(() => {
// 每秒推送一个 hi
subscriber.next('hi')
}, 1000);
});
Observer 观察者
Observer用户获取到Observable推送的值,Observers是一系列回调函数,也就是Observable.subscribe的回调函数
Observable.subscribe(x => console.log(x));
Observable.subscribe方法有三个回调函数,上面写的只是其中最常用的一个 next 回调函数
- “Next”: 接收Observable推送过来但值
- “Error”: 接收错误对象
- “Complete”: 推送结束时触发(即使出现error),不会收到任何值
// 下面这种写法是按顺序定义subscribe的三个回调函数 next、error、complete ,顺序不能乱
observable.subscribe(value => {
console.log('value', value);
}, error => {
console.error('error err', error);
}, () => {
console.log('complete');
});
// 完整写法,如果不需要error,可以删除error,只保留next和complete回调
observable.subscribe({
next(value) {
console.log('value', value);
},
error(error) {
console.error('error', error);
},
complete() {
console.log('complete');
}
});
异常捕获
const observable = new Observable(function subscribe(subscriber) {
subscriber.next(1);
subscriber.next(2);
subscriber.error(new Error('出错了'));
});
observable.subscribe(value => {
console.log('value', value);
}, error => {
console.error('error err', error);
}, () => {
console.log('complete');
});
可以用try/catch捕获错误
import {
Observable } from 'rxjs';
const observable = new Observable(function subscribe(subscriber) {
try {
subscriber.next(1);
subscriber.next(2);
subscriber.next(3);
subscriber.complete();
} catch (err) {
subscriber.error(err); // delivers an error if it caught one
}
});
手动结束推送
import {
Observable } from 'rxjs';
const observable = new Observable(function subscribe(subscriber) {
subscriber.next(1);
subscriber.next(2);
subscriber.next(3);
// 不调用complete方法,下面的observable.subscribe就不会输出complete
subscriber.complete();
subscriber.next(4); // 结束后再推送值,observable.subscribe也接收不到了
});
observable.subscribe(value => {
console.log('value', value);
}, error => {
console.error('error', error);
}, () => {
console.log('complete');
});
Subscription
observable.subscribe返回一个Subscription对象。可用于取消订阅,或添加/删除其他的子Subscription。
const subscription = observable.subscribe(x => console.log(x));
调用subscription.unsubscribe()即可取消订阅
const observable = interval(1000);
const subscription = observable.subscribe(x => console.log(x));
setTimeout(() => {
subscription.unsubscribe();
}, 5000);
什么是Operator
之前讲过什么是函数试编程,那么Operator就是一个个的函数,所以这些函数都具有函数式的特点:
- 都有返回值
- 不会对元数据产生副作用
即每个操作符(函数),在拿到原Observable对象后,经过处理都是返回一个新的Observable。
比如下面这个自定义map函数:
// of操作符创建一个obervable对象,该 Observable 只发出给定的参数('Jerry', 'Anna')
const people = of('Jerry', 'Anna');
function map(source: Observable<string>, callback: (item: string) => string) {
// map函数返回值是一个 可观察对象 observable
return new Observable(observer => {
// 订阅传入的 observable,并在next中调用回调函数处理数据流
return source.subscribe(
value => {
try{
observer.next(callback(value));
} catch (e) {
observer.error(e);
}
},
(err) => {
observer.error(err); },
() => {
observer.complete(); }
);
});
}
const helloPeople = map(people, (item) => item + ' Hello~');
// 订阅 helloPeople,拿到的res是已经调用回调函数处理过的数据流
helloPeople.subscribe(res => {
console.log('res', res);
});
这个map函数符合函数式的特点,在rxjs中,可称它为一个操作符(operator)。
Operator分类
管道操作符:filter, take…
创建类操作符:of,from…
Marble diagrams
异步往往是复杂的,尤其是多个异步结合在一起的逻辑,用文字难以表达 所以出现了一种叫Marble diagrams的图形表示法,协助理解各種 operators! 参考:
创建类操作符
参考官网 和 https://rxjs-cn.github.io/learn-rxjs-operators/
of
按顺序发出任意类型和数量的值
import {
of } from 'rxjs';
const source = of(1, 2, 3, 4, 5);
// 输出: 1,2,3,4,5
source.subscribe(val => console.log(val));
// 复杂类型
const source = of({
name: 'Brian' }, [1, 2, 3], function hello() {
return 'Hello';
});
// 输出: {name: 'Brian}, [1,2,3], function hello() { return 'Hello' }
const subscribe = source.subscribe(val => console.log(val));
from
将数组、promise 或迭代器转换成 observable 转数组
import {
from } from 'rxjs';
const arraySource = from([1, 2, 3, 4, 5]);
// 输出: 1,2,3,4,5
const subscribe = arraySource.subscribe(val => console.log(val));
转Promise:
import {
from } from 'rxjs';
const promiseSource = from(new Promise(resolve => resolve('Hello World!')));
// 输出: 'Hello World'
const subscribe = promiseSource.subscribe(val => console.log(val));
转Map对象:
import {
from } from 'rxjs';
const map = new Map([
[1, 'hi']
]);
map.set(2, 'Bye');
map.set(3, 'rxjs');
const mapSource = from(map);
// 输出: [1, 'Hi'], [2, 'Bye'], [3, 'rxjs']
const subscribe = mapSource.subscribe(val => console.log(val));
转字符串:
import {
from } from 'rxjs';
// 将字符串作为字符序列发出
const source = from('Hello World');
// 输出: 'H','e','l','l','o',' ','W','o','r','l','d'
source.subscribe(val => console.log(val));
empty
不带任何数据的的Observable, 会立即执行complete回调
import {
empty } from 'rxjs';
const result = empty();
result.subscribe(res => console.log(res), error => {
}, () => console.log('ok'));
fromEvent
将事件转换成 observable
import {
fromEvent } from 'rxjs';
import {
map } from 'rxjs/operators';
// 创建发出点击事件的 observable,document可以替换为任意dom,比如: button的click事件
const source = fromEvent(document, 'click');
const example = source.pipe(map(event => `Event time: ${
event.timeStamp}`));
example.subscribe(val => console.log(val));
interval
import {
interval } from 'rxjs';
// 每1秒发出数字序列中的值
const source = interval(1000);
// 数字: 0,1,2,3,4,5....
const subscribe = source.subscribe(val => console.log(val));
timer
import {
timer } from 'rxjs';