首页/八方信息/正文
Redux 单向数据流中异步场景处理及中间件应用常见做法

 2025年09月03日  阅读 8

摘要:【单向数据流的核心理念】view-˃action-˃reducer-˃state-˃view复制代码在整体规划中,单一路径的数据传递是最关键的布局理念。这种情形下,情形的转变务必借助特定的渠道来引发,从而构成一个明确的信息流转整体:界面...

【单向数据流的核心理念】

view ->  action -> reducer -> state -> view
复制代码

在整体规划中,单一路径的数据传递是最关键的布局理念。这种情形下,情形的转变务必借助特定的渠道来引发,从而构成一个明确的信息流转整体:界面部分引发行为,状态中心进行应对,然后刷新界面。这种严苛的单向路径确实保障了情形转变的可以预见性和能够回溯。

【异步处理的直接方案】

实际开发过程中时常碰到异步情况,最直接的办法是在组件里直接启动异步操作。数据送回之后,就依照既定方式去变更数据层。尽管这种做法一目了然,却会使业务规则散落在各个组件里,给后续维护和测试带来麻烦。组件既要负责界面显示,又要处理信息获取,这样违背了职责单一法则。

export default store => next => action => {
    const result = next(action);
    if (action.type === 'ping') {
        store.dispatch({ type: 'pong' })
    }
    return result;
}
复制代码

【中间件解决方案】

更理想的做法是借助中介件来应对非同步的数据流。中介件能够精确调控状态变更的时刻,务必等到非同步作业告一段落才去更新状态。它的关键机制在于,在调用next()动作之后执行非同步的程序,直到非同步操作全部结束后才确认状态转换。这种策略把非同步的程序都归集起来处理,同时维持了组件的纯净特质。

【RxJS的增强能力】

运用RxJS这个工具,程序的非同步执行效率大大增强。该库内含众多功能强大的函数,例如映射、筛选、合并等,能够巧妙应对各类非同步情形。核心优势在于其流水线式的数据流转机制:初始行为序列在经过函数的加工后,会形成满足特定用途的全新数据序列。

action$.pipe(
    switchMap(
        () => fromPromise(fetch('/api/whatever')).pipe(
            map(res => action)
        )
    ),
    catchError(() => {})
)
复制代码

【Epic模式详解】

import { Subject } from 'rxjs/Subject';
export default (store) => {
  const action$ = new Subject();
  
  action$.subscribe(store.dispatch);
  
  return next => (action) => {
    const result = next(action);
    action$.next(action);
    return result;
  };
};
复制代码

Epic是负责处理异步流程的关键部分,其根本是一个能够接收指令序列并输出新指令序列的函数。在Epic的运作中,借助RxJS的管道机制,可以执行多种操作。

1. 过滤特定类型的动作

2. 执行异步操作(如API调用)

const epic = (action$) => {
    return action$.pipe(
        // 因为所有的action都会过来
        // 所以我们只需要处理我们想要的aciton
        filter(action => action.type === 'GET_USER'),
        switchMap(
            // 将fetch也转化为流
            () => fromPromise(fetch('/api/user/get', {
                method: 'POST',
                body: {
                    id: 1
                },
            })).pipe(
                map(res => ({ type: 'GET_USER_SUCCESS', payload: res })),
                catchError(error => ({ type: 'GET_USER_FAILED', payload: error }))
            )
        )
    )
}
复制代码

redux-observable middleware asynchronous flow_微信小程序开发之redux绑定_create action stream

3. 转换返回数据为新的动作

标准的Epic模式由:行为性质核实、非同步任务指派、成效形态变更这三个环节组成。

import { Subject } from 'rxjs/Subject';
export default (store) => {
  const action$ = new Subject();
  const newAction$ = epic(action$);
  
  newAction$.subscribe(store.dispatch);
  
  return next => (action) => {
    const result = next(action);
    action$.next(action);
    return result;
  };
};
复制代码

【多Epic合并策略】

实际项目要应对多个并行事件,RxJS有合并功能可以将不同Epic生成的数据流整合在一起。合并后的统一数据流会进行整体订阅,以便所有并行操作都能得到妥善应对。这就要求设立一个总Epic来汇集所有业务Epic,以此维持代码的条理性。

【状态访问优化】

export const combineEpics = (...epics) => {
  const merger = (...args) => merge(
    ...epics.map((epic) => {
      const output$ = epic(...args);
      return output$;
    })
  );
  return merger;
};
复制代码

异步操作时常要查询即时情形,能借助过渡组件把状态查询方法提供给Epic。这样Epic既能发起新指令,又能拿到最新情形数据,达成完备的流程循环。在结构繁复的情境下,可以思量把状态信息也改造成可追踪的序列。

【执行顺序控制】

必须留意,Epic的运行次序会受RxJS调度机制的作用。通常的同步调度机制可能造成意料之外的现象,比如:

1. 动作处理顺序不符合时间线

const pingEpic = action$ => action$.pipe(
  filter(action => action.type === 'ping'),
  map(() => ({ type: 'pong' })),
);
const getUserEpic = action$ => action$.pipe(
  filter(action => action.type === 'GET_USER'),
  map(() => ({ type: 'GET_USER_SUCCESS', payload: { user: { name: 'kang' } } })),
);
const rootEpic = combineEpics(pingEpic, getUserEpic);
export default (store) => {
  const action$ = new Subject();
  const newAction$ = rootEpic(action$);
  newAction$.subscribe(store.dispatch);
  return next => (action) => {
    const result = next(action);
    action$.next(action);
    return result;
  };
};
复制代码

2. 异步响应动作可能被提前处理

微信小程序开发之redux绑定_create action stream_redux-observable middleware asynchronous flow

有效的处理办法是借助恰当的调度机制,比如asyncScheduler,运用delay(0)这类手段来管理操作流程。

export default (store) => {
  ...
  const newAction$ = rootEpic(action$, store.getState);
  ...
};
复制代码

【实践建议】

在具体实现时建议:

const pingEpic = (action$, getState) => action$.pipe(
  filter(action => action.type === 'ping'),
  map(() => ({ type: 'pong', payload: getState() })),
);
复制代码

1. 保持Epic的单一职责

2. 使用类型系统严格定义动作

export default store => next => action => {
    const oldState = store.getState();
    const result = next(action);
    const newState = store.getState();
    // 类似这样的写法
    if (newState.xxx !== oldState.xxx) {
        fetch('/api/save', {
            method: 'POST',
            body: {
            
            }
        }).then(() => {}).catch(() => {})
    }
    return result;
}
复制代码

3. 为复杂异步流添加详细注释

4. 对关键操作添加错误处理

这种设计兼顾了数据单向流动的长处,也能灵活应对繁杂的并发情形,非常适用于构建大型软件项目。

const saveEpic = (action$, state$) => state$.pipe(
const autoSaveEpic = (action$, state$) =>
  return action$.pipe(
    filter(action => action.type === 'AUTO_SAVE_ENABLE'), // 自动保存的开启
    exhaustMap(() => state$.pipe(
        pluck('xxx'), // 获取state.xxx
        distinctUntilChanged(), // 前后值不同时才将其发出。
        concatMap((value) => {
            // fetch to save
        }),
        // 自动保存的关闭
        takeUntil(action$.pipe(
            filter(action => action.type === 'AUTO_SAVE_DISABLE')
        ))
    ))
  )
)
复制代码

const countEpic = (action$, state$) => action$.pipe(
  filter(action => action.type === 'count'),
  withLatestFrom(state$),
  switchMap(([action, state]) => {
    return of({ type: 'whatever' });
  })
);
复制代码

版权声明:本文为 “博览广文网” 原创文章,转载请附上原文出处链接及本声明;

原文链接:http://wen.bjhwtx.com/post/37746.html

标签:

博览广文网

博览广文网为所有文学爱好者、新闻爱好者、关注生活多方面内容的观众朋友提供多方位的内容呈现、提升阅读空间、填充碎片时间,开阔读者的视野、增长见识、了解民生、一个让您不出户尽知天下事的网站平台!
热门标签
关于我们
广文舒阅网—让天下读者有家可归!这里汇聚了各类优质文化信息,无论是全球热点、历史故事,还是实用百科、趣味探索,您都能轻松获取。我们希望用阅读点亮您的世界,让每一次浏览都充满收获和乐趣。
导航栏A标题
广文舒阅网
扫码关注
联系方式
全国服务热线:0755-88186625
Q Q:8705332
Email:admin@lanyu.com
地址:深圳市福田区海雅缤纷国际大厦5层501
Copyright 深圳市蓝宇科技有限公司 版权所有 备案号:京ICP备20013102号-1