redux-observable笔记

495 阅读1分钟

参考

observable.subscribe 有三个对象 next,error,和 complete

observable.subscribe({
  next: value => console.log(`Value is ${value}`),
  error: err => console.log(err),
  complete: () => console.log(`Completed`),
})

该.subscribe功能也可以具有三个功能,而不是一个对象。

observable.subscribe(
  value => console.log(`Value is ${value}`),
  err => console.log(err),
  () => console.log(`Completed`)
)

我们可以通过创建一个对象来创建一个新的observable,传入一个subscriber(也称为观察者)的函数。该用户有三种方法:next,error,和complete。订户可以调用下一个与一个值作为根据需要很多次,complete或者error在端部。调用complete或后error,可观察对象将不会将任何值向下推。

import { Observable } from 'rxjs'
const observable$ = new Observable(function subscribe(subscriber) {
  const intervalId = setInterval(() => {
    subscriber.next('hi');
    subscriber.complete()
    clearInterval(intervalId);
  }, 1000);
});
observable$.subscribe(
  value => console.log(`Value is ${value}`),
  err => console.log(err)
)

上面的示例将 Value is hi 在1000毫秒后打印。

RxJS具有创建可观察对象的许多功能。一些最常用的是of,from 和 ajax

of 接受一系列值并将其转换为流

of(1, 2, 3, 'Hello', 'World').subscribe(value => console.log(value))
// 1 2 3 Hello World

from 将所有内容转换为流

import { from } from 'rxjs'
from([1, 2, 3]).subscribe(console.log)
// 1 2 3
from(new Promise.resolve('Hello World')).subscribe(console.log)
// 'Hello World'
from(fibonacciGenerator).subscribe(console.log)
// 1 1 2 3 5 8 13 21 ...

ajax 接受字符串URL或创建一个发出HTTP请求的可观察对象。ajax有一个函数 ajax.getJSON ,该函数仅从AJAX调用返回嵌套的响应对象,而没有由返回的其他任何属性ajax():

ajax('https://jsonplaceholder.typicode.com/todos/1').subscribe(console.log)
// {request, response: {userId, id, title, completed}, responseType, status}
ajax.getJSON('https://jsonplaceholder.typicode.com/todos/1').subscribe(console.log)
// {userId, id, title, completed}
ajax({ url, method, headers, body }).subscribe(console.log)
// {...}

Operators

Operators 是RxJS的真正强大之处,它具有可满足您几乎所有需要的运算符。从RxJS 6开始,运算符不是可观察对象上的方法,而是使用.pipe方法应用于可观察对象上的纯函数。

map 采用单个参数函数,并在流中的每个元素上应用投影:

of(1, 2, 3, 4, 5).pipe(
  map(i=> i * 2)
).subscribe(console.log)
// 2, 4, 6, 8, 10

image.png

filter 接受一个参数,并从流中删除为 给定函数返回false 的值

of(1, 2, 3, 4, 5).pipe(
  map(i => i * i),
  filter(i => i % 2 === 0)
).subscribe(console.log)
// 4, 16

image.png

flatMap 采用一个函数,将流(steam)中的每个项目映射到另一个流中,并展平这些流的所有值:

of(1, 2, 3).pipe(
  flatMap(page => ajax.toJSON(`https://example.com/blog?size=2&page=${page}`)),
).subscribe(console.log)
// [ { blog 1 }, { blog 2 }, { blog 3 }, { blog 4 }, { blog 5 }, { blog 6 } ]

image.png

merge 合并 两个流 中的项目,按到达的顺序

merge(
  interval(150).pipe(take(5), mapTo('A')),
  interval(250).pipe(take(5), mapTo('B'))
).subscribe(console.log)
// A B A A B A A B B B

image.png 其他操作符

redux-observable

image.png

redux 中的 所有操作都是同步的。 redux-observable 是 redux 的中间件,它使用 可观察的流 执行 异步工作,然后使用该异步工作的结果 在Redux 中 dispatch 另外一个 action.

Redux-observable 是 基于 Epics 的思想,一个epic 是一个function,它接受一系列的action,并且 可选 的产生 一个状态流 (a stream of state),并且返回 一系列 action。

function (action$:Observable,state$:StateObservable):Observable;

按照约定, 作为 steam(流)的每个变量 都以 结尾 **$ **。

下面 有个 ping-pong epic 例子: 携带一个 ping 请求服务,然后请求完成后,接收一个 pong 到 app中。

const pingEpic =action$=>action$.pipe(
  ofType('PING'),
  flatMap(action=>ajax('https://example.com/pinger')),
  mapTo({type:'PONG'})
 )

现在,我们将通过添加epics和检索用户来更新原始的Todo store。

import { combineReducers, createStore } from 'redux'
import { ofType, combineEpics, createEpicMiddleware } from 'redux-observable';
import { map, flatMap } from 'rxjs/operators'
import { ajax } from 'rxjs/ajax'
//...
/* user 和 todos reducers 定义 如上 */
const rootReducer = combineReducers({ user, todos }) 
const epicMiddleware = createEpicMiddleware();
const userEpic=action$=>action$.pipe(
  ofType('GET_USER'),
  flatMap(()=>ajax.getJSON('https://foo.bar.com/get-user')),
  map(user=>({type:'GET_USER_SUCCESS',payload:user}))
 )
const addTodoEpic=action$=>action$.pipe(
  ofType('ADD_TODO'),
  flatMap(()=>ajax({
    url:'https://foo.bar.com/add-todo',
    method:'POST',
    body:{text:action.payload}
  })),
  map(data=>data.response),
  map(todo=>({type:'ADD_TODO_SUCCESS',payload:todo}))
 )
const completeTodoEpic=action$=>action$.pipe(
  ofType('COMPLETE_TODO'),
  flatMap(()=>ajax({
    url:'https://foo.bar.com/add-todo',
    method:'POST',
    body:{id:action.payload}
  })),
  map(data=>data.response),
  map(todo=>({type:'COMPLETE_TODO_SUCCESS',payload:todo}))
 )
const rootEpic = combinEpic (userEpic,addTodoEpic,completeTodoEpic)
const store = createStore(rootReducer,applyMiddleware(epicMiddleware))
epicMiddleware.run(rootEpic);

注意: Epic 和 RxJS 中的其他 observable streams 一样 ,它们最终会 处于 complete 或 error 的状态,然后 epic 和app 将后停止 工作,因此可以使用 catchError 抓住潜在的错误 反应式TODO 应用-github源码