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