rx学习指南
https://rxjs-cn.github.io/learn-rxjs-operators/
https://robin-front.gitbooks.io/rxjs-doc-chinese/
https://zhuanlan.zhihu.com/p/146007671 返回按钮
https://limeii.github.io/2019/08/rxjs-unsubscribe/ 退订理解
https://www.dazhuanlan.com/2019/12/10/5deed365a3c52/
1、map
map和javascript中的数组的map方法类似
const getData = (param) => {
return of(`return: ${param}`).pipe(
delay(Math.random() * 1000)
)
};
from([1, 2, 3, 4,5])
.pipe(
map(param => getData(param)),
)
.subscribe(val => console.log(val));
返回的是6 observable
2、concatAll
javascript中数组也有一个方法叫做concat,concatAll有个flatten效果
from([1, 2, 3, 4, 5])
.pipe(
map(param => getData(param)),
concatAll() // 比上个例子多出的部分
)
.subscribe(val => console.log(val));
return: 1
return: 2
return: 3
return: 4
return: 5
3、concatMap
map和concatAll直接结合成一个操作符
from([1,2,3,4,5])
.pipe(
concatMap(param => getData(param))
)
.subscribe(val => console.log(val));
4、mergeAll
from([1, 2, 3, 4, 5])
.pipe(
map(
item => getData(item)
),
mergeAll()
)
.subscribe(v => console.log(v));
随机 也可以实现concatAll的效果,只要 mergeAll(1) 就可以了
5、mergeMap(又叫flatMap)
from([1, 2, 3, 4,5])
.pipe(
mergeMap(param => getData(param))
)
.subscribe(val => console.log(val));
6、switchAll
from([1,2,3,4,5]).pipe(
map(param => getData(param)),
switchAll()
).subscribe(val => console.log(val));
return 5
map之后产生的五个observable, 经过switchAll之后,由于五个observable的delay不同,所以还没来得及发射数据,就被最后的observable给‘踢’掉了
7、switchMap
map之后switchAll也可以合并成一个操作
会在新的 Observable 对象发出新值后 退订前一个未处理完的 Observable 对象
from([1,2,3,4,5])
.pipe(
switchMap(param => getData(param))
)
.subscribe(val => console.log(val));
8、forkJoin
forkJoin 是 Rx 版本的 Promise.all()
,即表示等到所有的 Observable 都完成后,才一次性返回值
const getPostOne$ = Rx.Observable.timer(1000).mapTo({id: 1});
const getPostTwo$ = Rx.Observable.timer(2000).mapTo({id: 2});
Rx.Observable.forkJoin(getPostOne$, getPostTwo$).subscribe(
res => console.log(res) // [{id: 1}, {id: 2}]
);
9、处理两个请求
1、当我们发送下一个请求时,需要依赖于上一个请求的数据。即我们在需要在上一个请求的回调函数中获取相应数据,然后在发起另一个 HTTP 请求
import { Component, OnInit } from '@angular/core';
import { Http } from '@angular/http';
import 'rxjs/add/operator/map';
@Component({
selector: 'app-root',
template: `
<p>{{username}} Detail Info</p>
{{user | json}}
`
})
export class AppComponent implements OnInit {
constructor(private http: Http) { }
apiUrl = 'https://jsonplaceholder.typicode.com/users';
username: string = '';
user: any;
ngOnInit() {
this.http.get(this.apiUrl)
.map(res => res.json())
.subscribe(users => {
let username = users[6].username;
this.http.get(`${this.apiUrl}?username=${username}`)
.map(res => res.json())
.subscribe(
user => {
this.username = username;
this.user = user;
});
});
}
}
先从 https://jsonplaceholder.typicode.com/users
地址获取所有用户的信息,然后再根据指定用户的 username
进一步获取用户的详细信息
import { Component, OnInit } from '@angular/core';
import { Http } from '@angular/http';
import 'rxjs/add/operator/map';
import 'rxjs/add/operator/mergeMap';
@Component({
selector: 'app-root',
template: `
<p>{{username}} Detail Info</p>
{{user | json}}
`
})
export class AppComponent implements OnInit {
constructor(private http: Http) { }
apiUrl = 'https://jsonplaceholder.typicode.com/users';
username: string = '';
user: any;
ngOnInit() {
this.http.get(this.apiUrl)
.map(res => res.json())
.mergeMap(users => {
this.username = users[6].username;
return this.http.get(`${this.apiUrl}?username=${this.username}`)
.map(res => res.json())
})
.subscribe(user => this.user = user);
}
}
通过 mergeMap
操作符,解决了嵌套订阅的问题
10、Promise的链式调用 很好解决并行和串行请求,但是Promise本身是无法取消的
存在问题:
在单页面应用中,resolve回调里需要进行try/catch处理,特别是在回调里有DOM操作的时候。否则,在接口响应慢的时候进行路由切换会导致控制台报错,甚至导致页面陷入无法交互的境地
由于接口响应慢而导致的竞态条件问题
rxjs是可以取消的,对于Promise出现的两个问题
在切换路由,组件销毁时调用unsubscribe方法取消订阅,回调里的逻辑便不会执行
竞态条件是由于接口异步调用的回调顺序不可控导致的,rxjs的switchMap操作符可以确保每次接收到的都是最新一次发送的值(即最新的接口回调的值)
11、distinct(lambda) distinctUntilChanged([(Prev, current)=>{}]) 和 distinctUntilKeyChanged(key)
过滤掉重复的项
from([1, 2, 2, 3, 2])
.pipe(distinct())
.subscribe(l); // 1,2,3
12、debounce(lambda: Observable) 和 debounceTime(number)
延时发送源发出的值, 如果期间源发出了新值的话,返回的值为最新的值,上一个会被丢弃掉(避免高消费事件时使用)
13、find 和 findIndex
类似 Array.prototype.find()
14、toPromise
把 Observable 转化为 promise
click = async e => {
let res = await ajax('http://localhost:1995/a').pipe(map(res => res.response)).toPromise();
l(res)
}
15、buffer bufferCount bufferTime bufferToggle bufferWhen
buffer系列,将过去的值作为数组收集,在事件触发时发出收集好的值
const send$= fromEvent(document, 'click');
const interval = interval(1000);
const buffered = interval.pipe(buffer(send$));
buffered.subscribe(l);
16、delay delayWhen
延迟来自源Observable的项目的发射
17、endWith
from([1, 2])
.pipe(endWith("源观察完成后,附加一个发射,然后完成。"))
.subscribe(l); // 1, 2, "源观察完成后,附加一个发射,然后完成。"
18、onErrorResumeNext
当任何提供的 Observable 发出完成或错误通知时,它会立即地订阅已传入下一个 Observable
Rx.Observable.of(1, 2, 3, 0)
.map(x => {
if (x === 0) { throw Error(); }
return 10 / x;
})
.onErrorResumeNext(Rx.Observable.of(1, 2, 3))
.subscribe(
val => console.log(val),
err => console.log(err), // 永远不会调用
() => console.log('that\'s it!')
);
// 输出:
// 10
// 5
// 3.3333333333333335
// 1
// 2
// 3
// "that's it!"
19、Rxjs的pipe怎么像Promise的then一样等待ajax结束才继续?
from(this.getOne)
.pipe(
mergeMap(oneData => {
console.log(oneData)
return from(this.getTwo)
}),
mergeMap(twoData => {
console.log(twoData)
return from(this.getThree)
})
)
.subscribe(threeData => {
console.log(threeData)
...
})
20、pipe
Pipe是RxJs的一个特性。根据角度文件
Pipe允许您将多个函数组合成一个函数。函数的参数是要组合的函数,并返回一个新函数,该函数在执行时按顺序运行组合的函数
import { filter, map } from 'rxjs/operators';
const squareOdd = of(1, 2, 3, 4, 5)
.pipe(
filter(n => n % 2 !== 0),
map(n => n * n)
);// Subscribe to get values
squareOdd.subscribe(x => console.log(x));
21、do
this.searchService.windowScroll()
.do(() => this.isLoading = true)
.map((page) => this.searchService.query(page))
.switch()
.subscribe((res) => {
this.results = [...this.results, ...res.data]
this.isLoading = false
})
22、
// 间隔 1s 请求
this.timer$ = interval(1000)
.pipe(
// 取消过时的请求值
switchMap(() => {
return this.http.get(API);
}),
)
.subscribe(
(res: any) => {
// 百分数处理逻辑
},
() => {
this.timer$.unsubscribe();
},
() => {
this.timer$.unsubscribe();
},
);
23、搜索
let input = document.querySelector('#input');
//return some ajax data
let ajaxSearch = function (input) {
//mock data
return Promise.resolve(`search data: ${input}`);
};
let source = Rx.Observable.fromEvent(input, 'keyup');
source
.map(event => event.target.value)
.debounce(500)
.distinctUntilChanged()
.filter(input => {
input = input.replace(/\s+/, "");
return input !== '';
})
.flatMapLatest(ajaxSearch)
.subscribe(log);
首先将 Input 的 keyup 作为事件源
使用 map 获得输入的值
使用 debounce 去抖,即500ms获取一个值,防止频繁发送请求
使用 distinctUntilChanged 去掉重复的值,防止搜索重复的内容
使用 filter 滤除空字符串
使用 flatMapLatest 获取请求中的内容,flatMapLatest 用法与 flatMap 类似,但是如果上一次的请求还未返回,又发出一个新请求,则忽略上一个请求
24、Promise 转化为 Observalble
// 数组(Iterables, 也可以是 Map Set Generators)作为数据流
var source = Rx.Observable.from([1,2,3]);
// Promise 转化为 Observalble
let promise = Promise.resolve(0);
let source = Rx.Observable.fromPromise(promise);
//从callback转化
let callback=(i,fn)=>fn(i+1);
let addOne = Rx.Observable.fromCallback(callback);
addOne(0).subscribe(log); // => 1
25、网络实例代码
reloadUsers(): void {
console.log('reload users');
this.users$ = this.route.paramMap.pipe(
switchMap((params: ParamMap) => {
return this.serverUserService.findByServerId(params.get('serverId'));
})
);
}
deleteUser(serverUserId: string): void {
this.serverUserService.deleteUser(serverUserId).subscribe({
next: response => {
if (response.success) {
this.messageService.success('服务器用户删除成功!');
debugger;
this.users$ = this.users$.pipe(
switchMap(users => {
return of(users.filter(user => {
return user.serverUserId != serverUserId;
}));
})
);
} else {
this.messageService.error(response.message);
}
}
})
}
import {Observable, from} from 'rxjs'
// 新建⼀个可订阅对象
let obser = new Observable<string>(productor => {
// ⾃定义数据流
productor.next("hello")
productor.next("world")
setTimeout(()=>{
productor.next("After 1 Sec")
productor.complete()
}, 1000)
})
// 消费
obse.subscribe({
next(item) {
console.log(item)
},
error(err) {
console.log(err)
},
complete() {
console.log("Done")
}
}
public post<T>(
destination: string,
data?: any,
options: RequestOptions = {}
): Observable<AxiosResponse<T>> {
this.unsubscribe = new Subject<any>();
return new Observable<AxiosResponse<T>>((observer: any) => {
this.processCustomParameters(options);
axios
.post(destination, data, options as AxiosRequestConfig)
.then((response: AxiosResponse<T>) => {
observer.next(response);
observer.complete();
})
.catch((error: any) => {
observer.error(HttpClient.converterErrorResponse(error));
});
}).pipe(takeUntil(this.unsubscribe));
}
26、rsjs5
var subject = new Rx.BehaviorSubject(56);
subject.onCompleted();
var subscription = source.subscribe(
function (x) {
console.log('Next: ' + x);
},
function (err) {
console.log('Error: ' + err);
},
function () {
console.log('Completed');
});
// => Next: 56
subject.onNext(42);
// => Next: 42
subject.onCompleted();
// => Completed