map
案例:获取用户输入的信息并实时进行搜索。
之前我们用subject实现过,现在我们用户fromEvent和map来实现功能
- html文本框
<input type="text" id="searchBox">
- 通过fromEvent获取用户输入事件流
import { fromEvent } from 'rxjs';
setTimeout(() => {//这里延迟1秒获取的原因是因为getElementById需要等页面渲染完毕才能获取
var search = document.getElementById("searchBox")
fromEvent(search, "input").subscribe(x => { console.log(x) })
}, 1000);//获取到的是事件对象
- 通过map操作将原来的数据事件流转换成输入文本流
import { fromEvent } from 'rxjs';
import { map } from "rxjs/operators";
setTimeout(() => {
var search = document.getElementById("searchBox")
fromEvent<any>(search, "input")
.pipe(
map(x=>x.target.value)
)
.subscribe(x => { console.log(x) })
}, 1000);
mergeMap
map能将发射过来的数据进行转换,一般都是从一个基本javascript对象到另一个基本javascript对象。但是有时候我们需要转换成的对象是另一个源
例如:我们我们先执行一个api查询,等上一个查询完毕后将查询出来的数据取出某个属性(比如id)再执行另一个api查询
- 最简单的写法
import { HttpClient } from '@angular/common/http';
import { Observable } from 'rxjs';
api1():Observable<any>{
return this.http.get('api1')
}
api2(id:number):Observable<any>{
return this.http.get('api1')
}
constructor(private http: HttpClient) {
this.api1().subscribe(x => {
console.log(x)
this.api2(x.id).subscribe(y => {
console.log(y)
})
})
}
- 用mergeMap实现
import { HttpClient } from '@angular/common/http';
import { Observable } from 'rxjs';
import { mergeMap } from "rxjs/operators";
api1(): Observable<any> {
return this.http.get('api1')
}
api2(id: number): Observable<any> {
return this.http.get('api1')
}
constructor(private http: HttpClient) {
this.api1().pipe(
mergeMap(x=>this.api2(x.id))
).subscribe(x=>{
console.log(x)
})
}
好处在哪里?如果这个是有了第三个api,需要再第二个api的基础上的话,第一种写法是继续订阅,而第二种的写法只需要再pipe中继续增加mergeMap就行了。整体代码显得更加优雅
- mergeMap=map()+mergeAll()。
上面的例子如果用map的话如下
this.api().pipe(
map(x=>this.api2(x.id))
).subscribe(x=>{
console.log(x)
})
输出出来的数据是observable对象,如果像得到具体的值,需要再跟一个mergeAll()函数,如下
this.api().pipe(
map(x=>this.api2(x.id))
mergeAll()
).subscribe(x=>{
console.log(x)
})
mergeMap/concatMap/switchMap/exhaustMap
区别主要在于执行的顺序
import { interval, fromEvent } from 'rxjs';
import { mergeMap, take, concatMap, switchMap } from "rxjs/operators";
# interval(1000).pipe(take(4)) 这个是一个0,1,2,3的源,每隔一秒发射一次
# mergeMap点击后产生的[0,1,2,3]源单独发射出来,每次点击之间互不影响。如果你点击快的话,能看到一下子出来好多0的情况
fromEvent(document, "click").pipe(
mergeMap(x => )
).subscribe(x => {
console.log(x)
})
# concatMap和上面的不一样,每一次点击后的源都会等上一次的源所有的0,1,2,3都发射完毕了以后才发射。相当于mergeMap是并行发射的,而concatMap是串行发射的,不能交叉
fromEvent(document, "click").pipe(
concatMap(x => interval(1000).pipe(take(4)))
).subscribe(x => {
console.log(x)
})
# 而mergeMap干脆就是放弃之前的源。如果你上一次点击后0,1,2,3还没有发射完毕,又点击了一次,这和时候之前没有发射的数据就不发射了,之前又从0开始发射了。
fromEvent(document, "click").pipe(
switchMap(x => interval(1000).pipe(take(4)))
).subscribe(x => {
console.log(x)
})
# 和switchMap相反,exhaustMap是当前源的发射没有结束的话后面的源自动放弃。所以你会看到上一次的点击发射的0,1,2,3在没有结束之前你的点击都是无效的。
fromEvent(document, "click").pipe(
exhaustMap(x => interval(1000).pipe(take(4)))
).subscribe(x => {
console.log(x)
})
switchMap用于做搜索
因为我们的用户输入搜索条件的时候可能很频繁,而且可能还没有等到数据从后台返回就又改变了搜索条件。这个时候
- 为了保证数据安全,所以绝对不能用mergeMap,这个返回的数据有可能是前一个条件的数据,
- 也不能用concatMap,虽然它能保证最后一定是最后一次的搜索条件,但是在前一个搜索没有执行完毕之前后面的搜索可能都阻塞在这里,用户体验极差。
- exhaustMap也不是需要的函数,因为我们希望根据最新输入的关键词查出来数据,而不是之前输入的关键词
网友评论