创建Observable
new Observable
const observable = new Observable(subscriber => {
subscriber.next(1)
subscriber.next(2)
subscriber.next(3)
subscriber.complete()
})
observable.subscribe({
next: value => console.log('next value:', value),
complete: () => { console.log('complete') }
})
observable.subscribe(value => console.log('next value:', value))
pipe
const stream = new Observable(observer => {
observer.next({ id: 1, name: "章三", age: 18 });
observer.complete();
});
stream
.pipe(filter(x => x.age >= 18))
.pipe(map(x => x.name))
.subscribe(result => console.log(result))
// 等同于
stream.subscribe(x=> {
if(x.age < 18) {
return;
}
const result = x.name;
console.log(result);
})
promise stirng 等转Observabl
from:可把一切转成Observable of: - 从非Observable值创建一个Observable / stream(它可以是一个原语,一个对象,一个函数,任何东西)。
import { of, from } from './rxjs'
const arrayLike = of(1, 2, 3)
arrayLike.subscribe({
next: value => console.log(`arrayLike:`, value),
complete: () => console.log('arrayLike done')
});
const promiseLike = from(Promise.resolve(4))
promiseLike.subscribe({
next: value => console.log(`promiseLike:`, value),
complete: () => console.log('promiseLike done'), });
事件转Observable
import { fromEvent } from './rxjs'
const source = fromEvent(document, 'click');
const subscriber = source.subscribe(console.log)
1csetTimeout(() => { subscriber.unsubscribe(); }, 1000)
串联,嵌套请求
concatMap
该操作符正是为了解决 concat 无法拦截指定流的问题, 它将源值投射为一个合并到输出流的结果,以串行的方式等待前一个完成再合并下一个流 。
如下代码,需要在获取到第一个API的数据做过滤,然后与第二个API返回的数据做组合
fetchPages$
.pipe(map(pages => pages.filter(x => x.visibility))))
.pipe(concatMap(pages => this.fetchRecentlyOperatedPages(pages))
case1
项目场景:
很常见的场景,先获取用户的Token再获取用户信息。
主要用到的操作符 concatMap 合并操作符。用于合并可观察对象
他接受一个回调函数入参就是上一个流发送得到的数据
区别:switchMap 总是会抛弃前一个请求的结果,concatMap不会
import { from } from 'rxjs';
import { mergeMap } from 'rxjs/operators';
import axios from 'axios';
const users$ = axios.get('https://jsonplaceholder.typicode.com/users');
const result$ = from(users$).pipe(
mergeMap(users => from(users.data)),
mergeMap(user => axios.get(`https://jsonplaceholder.typicode.com/users/${user.id}`))
);
result$.subscribe(console.log);
case2
fromEvent(button,"click")
.pipe(
concatMap(event => from(axios.get("http://locahost:4400/token")).pipe(
pluck("data","token")
)
),
concatMap( token => from(axios.get("http://userInfo")).pipe(pluck("data")) )
).subscribe(console.log)
循环调用api
case1
有多个ids : [1,2,3,4,5],然后对于每个要调用delete api endpoint.Normally的id,我必须使用forEach函数调用它5次,将它合并为一个可观察对象,这样就可以知道循环何时结束
deleteIds(ids: Array<number>) {
// create a array of delete observables to be executed at once
const deleteIds = ids.map(id => this.myService.delete(id));
forkJoin(deleteIds).subscribe(() => {
//等所有的流被处理完毕之后再进行操作。
}); }
import { forkJoin } from 'rxjs';
import { ajax } from 'rxjs/ajax';
const randomName$ = ajax('https://random-data-api.com/api/name/random_name');
const randomNation$ = ajax(
'https://random-data-api.com/api/nation/random_nation'
);
const randomFood$ = ajax('https://random-data-api.com/api/food/random_food');
forkJoin([randomName$, randomNation$, randomFood$]).subscribe({
next: ([nameAjax, nationAjax, foodAjax]) => {
console.log(
`${nameAjax.response.first_name} is from ${nationAjax.response.capital} and likes to eat ${foodAjax.response.dish}`
);
},
});
case2
[ { id: '123', name: 'nameVal1',
salary: [ { href: 'http://example.com/salary/1' }, { href: 'http://example.com/salary/2' } ],
address: { href: 'http:/example.com/address' } },
{ id: '456', name: 'nameVal2',
salary: { href: 'http://example.com/salary/1' },
address: { href: 'http:/example.com/address' } } ];
mainStream.pipe(
// fetch data for every person
switchMap(persons => forkJoin( persons.map(person => getPersonData(person)) )) );
// get data for a single person function
getPersonData(person): Observable<any> {
// the salary data as an observable
const salaryData = forkJoin(person.salary.map(({ href }) => getSalaryData(href));
// the address data as an observable
const addressData = getAddressData(person.address.href);
// combine salary and address data return
forkJoin(salaryData, addressData).pipe(
// map salary and address data to a person object with this data
map(([ salary, address ]) => ({ ...person, salary, address })) ); }
case 3
const action = { type: 'FETCH_DATA', payload: { value: [ '1', '2' ], } };
rxjs.of(action)
.pipe( rxjs.operators.mergeMap(action => {
const requestArrays = action.payload.value.map(
i => { return rxjs.ajax.ajax.getJSON(`https://jsonplaceholder.typicode.com/posts/${i}`); });
return rxjs.forkJoin(requestArrays)
.pipe( rxjs.operators.map(value => { return {type: 'FETCH_DATA_SUCCEEDED', payload: {value}}; }) ) }) )
.subscribe(console.log);
case 4
this.service.readArray()
.switchMap(array => {
//lets map the array member to the respective observable
const obs$ = array.map(item => {
return this.service2.readItem(item.id)
.pipe(
catchError(err => {
//Do whatever you want to do with this error
//make sure to return an observable as per your logic. For this example, I am simply returning the err wrapped in an observable. Having catchError operator will gracefully handle the exception and make sure to emit the value as part of forkJoin.
return of(err);
})
)
});
//forkJoin will wait for all the readItem calls get finished.
return forkJoin(obs$)
.pipe(
//return the original array along with joined using of
mergeMap((joined) => {
return of([array, joined]);
})
);
})
.subscribe((finalArray) => {
//finalArray will have readArray API response [i.e. array] at 0 index and on 1st index it will have joined array
console.log(finalArray);
//do whatever you want to do with the array
});
rxjs实现文件切片上传例子
redux-observable
epic case
const url = 'https://evening-citadel-85778.herokuapp.com/whiskey/'; // The API for the whiskies
/*
The API returns the data in the following format:
{
"count": number,
"next": "url to next page",
"previous": "url to previous page",
"results: array of whiskies
}
since we are only interested in the results array we will have to use map on our observable
*/
function fetchWhiskiesEpic(action$) { // action$ is a stream of actions
// action$.ofType is the outer Observable
return action$
.ofType(FETCH_WHISKIES) // ofType(FETCH_WHISKIES) is just a simpler version of .filter(x => x.type === FETCH_WHISKIES)
.switchMap(() => {
// ajax calls from Observable return observables. This is how we generate the inner Observable
return ajax
.getJSON(url) // getJSON simply sends a GET request with Content-Type application/json
.map(data => data.results) // get the data and extract only the results
.map(whiskies => whiskies.map(whisky => ({
id: whisky.id,
title: "whisky.title,"
imageUrl: whisky.img_url
})))// we need to iterate over the whiskies and get only the properties we need
// filter out whiskies without image URLs (for convenience only)
.map(whiskies => whiskies.filter(whisky => !!whisky.imageUrl))
// at the end our inner Observable has a stream of an array of whisky objects which will be merged into the outer Observable
})
.map(whiskies => fetchWhiskiesSuccess(whiskies)) // map the resulting array to an action of type FETCH_WHISKIES_SUCCESS
// every action that is contained in the stream returned from the epic is dispatched to Redux, this is why we map the actions to streams.
// if an error occurs, create an Observable of the action to be dispatched on error. Unlike other operators, catch does not explicitly return an Observable.
.catch(error => Observable.of(fetchWhiskiesFailure(error.message)))
}