redux处理异步action解决方案


Posted in Javascript onMarch 22, 2020

如果没有中间件,store.dispatch只能接收一个普通对象作为action。在处理异步action时,我们需要在异步回调或者promise函数then内,async函数await之后dispatch。

dispatch({
  type:'before-load'
})
fetch('http://myapi.com/${userId}').then({
  response =>dispatch({
      type:'load',
      payload:response
    })
})

这样做确实可以解决问题,特别是在小型项目中,高效,可读性强。 但缺点是需要在组件中写大量的异步逻辑代码,不能将异步过程(例如异步获取数据)与dispatch抽象出来进行复用。而采用类似redux-thunk之类的中间件可以使得dispatch能够接收不仅仅是普通对象作为action。例如:

function load(userId){
  return function(dispatch,getState){
    dispatch({
      type:'before-load'
    })
    fetch('http://myapi.com/${userId}').then({
      response =>dispatch({
        type:'load',
        payload:response
      })
    })
  }  
}
//使用方式
dispatch(load(userId))

使用中间件可以让你采用自己方便的方式dispatch异步action,下面介绍常见的三种。

1. redux-thunk

function createThunkMiddleware(extraArgument) {
 return ({ dispatch, getState }) => next => action => {
  if (typeof action === 'function') {
   return action(dispatch, getState, extraArgument);
  }

  return next(action);
 };
}

const thunk = createThunkMiddleware();
thunk.withExtraArgument = createThunkMiddleware;

export default thunk;

2. redux-promise

使用redux-promise可以将action或者action的payload写成promise形式。 源码:

export default function promiseMiddleware({ dispatch }) {
 return next => action => {
  if (!isFSA(action)) {
   return isPromise(action) ? action.then(dispatch) : next(action);
  }

  return isPromise(action.payload)
   ? action.payload
     .then(result => dispatch({ ...action, payload: result }))
     .catch(error => {
      dispatch({ ...action, payload: error, error: true });
      return Promise.reject(error);
     })
   : next(action);
 };
}

3. Redux-saga

redux-saga 是一个用于管理应用程序 Side Effect(副作用,例如异步获取数据,访问浏览器缓存等)的 library,它的目标是让副作用管理更容易,执行更高效,测试更简单,在处理故障时更容易

3.1 基本使用

import { call, put, takeEvery, takeLatest} from 'redux-saga/effects';

//复杂的异步流程操作
function* fetchUser(action){
 try{
  const user = yield call(API.fetchUser, action.payload);
  yield put({type:"USER_FETCH_SUCCEEDED",user:user})
 }catch(e){
  yield put({type:"USER_FETCH_FAILED",message:e.message})
 }
}

//监听dispatch,调用相应函数进行处理
function* mainSaga(){
 yield takeEvery("USER_FETCH_REQUESTED",fetchUser);
}

//在store中注入saga中间件
import {createStore,applyMiddleware} from 'redux';
import createSagaMiddleware from 'redux-saga';

import reducer from './reducers';
import mainSaga from './mainSaga';
const sagaMiddleware = createSagaMiddleware();

const store = createStore(reducer,initalState,applyMiddleware(sagaMiddleware));

sagaMiddleware.run(mainSaga)

3.2 声明式effects,便于测试

为了测试方便,在generator中不立即执行异步调用,而是使用call、apply等effects创建一条描述函数调用的对象,saga中间件确保执行函数调用并在响应被resolve时恢复generator。

function* fetchProducts() {
 const products = yield Api.fetch('/products')
 dispatch({ type: 'PRODUCTS_RECEIVED', products })
}

//便于测试
function* fetchProducts() {
 const products = yield call(Api.fetch, '/products')
 //便于测试dispatch
 yield put({ type: 'PRODUCTS_RECEIVED', products })
 // ...
}
// Effect -> 调用 Api.fetch 函数并传递 `./products` 作为参数
{
 CALL: {
  fn: Api.fetch,
  args: ['./products'] 
 }
}

3.3 构建复杂的控制流

saga可以通过使用effect创建器、effect组合器、saga辅助函数来构建复杂的控制流。

effect创建器:

  • take:阻塞性effect,等待store中匹配的action或channel中的特定消息。
  • put:非阻塞性effect,用来命令 middleware 向 Store 发起一个 action。
  • call:阻塞性effect,用来命令 middleware 以参数 args 调用函数 fn。
  • fork:非阻塞性effect,用来命令 middleware 以 非阻塞调用 的形式执行 fn
  • select:非阻塞性effect,用来命令 middleware 在当前 Store 的 state 上调用指定的选择器

effect组合器:

race:阻塞性effect:用来命令 middleware 在多个 Effect 间运行 竞赛(Race)

function fetchUsersSaga { 
 const { response, cancel } = yield race({ 
 response: call(fetchUsers), 
 cancel: take(CANCEL_FETCH) 
 }) 
}

all: 当 array 或 object 中有阻塞型 effect 的时候阻塞,用来命令 middleware 并行地运行多个 Effect,并等待它们全部完成

function* mySaga() { 
 const [customers, products] = yield all([ 
 call(fetchCustomers), 
 call(fetchProducts) 
 ]) 
}

effect辅助函数:

takeEvery:非阻塞性effect,在发起(dispatch)到 Store 并且匹配 pattern 的每一个 action 上派生一个 saga

const takeEvery = (patternOrChannel, saga, ...args) => fork(function*() { 
while (true) { 
const action = yield take(patternOrChannel) 
yield fork(saga, ...args.concat(action)) 
} 
  })

takeLatest:非阻塞性,在发起到 Store 并且匹配 pattern 的每一个 action 上派生一个 saga。并自动取消之前所有已经启动但仍在执行中的 saga 任务。

const takeLatest = (patternOrChannel, saga, ...args) => fork(function*() { 
  let lastTask 
  while (true) { 
  const action = yield take(patternOrChannel) 
  if (lastTask) { 
  yield cancel(lastTask) // 如果任务已经结束,cancel 则是空操作 
  } 
  lastTask = yield fork(saga, ...args.concat(action)) 
  } 
 })

throttle:非阻塞性,在 ms 毫秒内将暂停派生新的任务

const throttle = (ms, pattern, task, ...args) => fork(function*() { 
  const throttleChannel = yield actionChannel(pattern) 
  ​ 
  while (true) { 
  const action = yield take(throttleChannel) 
  yield fork(task, ...args, action) 
  yield delay(ms) 
  } 
  })

到此这篇关于redux处理异步action解决方案的文章就介绍到这了,更多相关redux 异步action内容请搜索三水点靠木以前的文章或继续浏览下面的相关文章希望大家以后多多支持三水点靠木!

Javascript 相关文章推荐
javascript 获取select下拉列表值的代码
Sep 07 Javascript
类似GMAIL的Ajax信息反馈显示
Feb 16 Javascript
基于JQuery制作的产品广告效果
Dec 08 Javascript
js实现带关闭按钮始终显示在网页最底部工具条的方法
Mar 02 Javascript
javascript中的Base64、UTF8编码与解码详解
Mar 18 Javascript
js如何判断输入字符串长度
Dec 16 Javascript
easy ui datagrid 从编辑框中获取值的方法
Feb 22 Javascript
vue-image-crop基于Vue的移动端图片裁剪组件示例
Aug 28 Javascript
vue强制刷新组件的方法示例
Feb 28 Javascript
小程序云开发教程如何使用云函数实现点赞功能
May 18 Javascript
微信小程序实现左侧滑动导航栏
Apr 08 Javascript
JavaScript检测是否开启了控制台(F12调试工具)
Oct 02 Javascript
JS+CSS实现3D切割轮播图
Mar 21 #Javascript
vue-autoui自匹配webapi的UI控件的实现
Mar 20 #Javascript
jQuery实现中奖播报功能(让文本滚动起来) 简单设置数值即可
Mar 20 #jQuery
微信小程序保持session会话的方法
Mar 20 #Javascript
微信小程序后端无法保持session的原因及解决办法问题
Mar 20 #Javascript
js 闭包深入理解与实例分析
Mar 19 #Javascript
JS一次前端面试经历记录
Mar 19 #Javascript
You might like
?繁体转换的class
2006/10/09 PHP
Smarty3配置及入门语法
2017/02/22 PHP
Javascript Function对象扩展之延时执行函数
2010/07/06 Javascript
EasyUI中的tree用法介绍
2011/11/01 Javascript
javascript 另一种图片滚动切换效果思路
2012/04/20 Javascript
js事件(Event)知识整理
2012/10/11 Javascript
JS动态获取当前时间,并写到特定的区域
2013/05/03 Javascript
Javascript和HTML5利用canvas构建Web五子棋游戏实现算法
2013/07/17 Javascript
javascript新建标签,判断键盘输入,以及判断焦点(示例代码)
2013/11/25 Javascript
详解JavaScript对W3C DOM模版的支持情况
2015/06/16 Javascript
详解JS-- 浮点数运算处理
2016/11/28 Javascript
详解使用grunt完成requirejs的合并压缩和js文件的版本控制
2017/03/02 Javascript
求js数组的最大值和最小值的四种方法
2017/03/03 Javascript
vue 使用eventBus实现同级组件的通讯
2018/03/02 Javascript
解决使用vue.js路由后失效的问题
2018/03/17 Javascript
layui监听单元格编辑前后交互的例子
2019/09/16 Javascript
vue使用一些外部插件及样式的配置代码
2019/11/18 Javascript
js实现多个标题吸顶效果
2020/01/08 Javascript
[41:37]DOTA2北京网鱼队选拔赛——冲击职业之路
2015/04/13 DOTA
python cs架构实现简单文件传输
2020/03/20 Python
Pytorch自己加载单通道图片用作数据集训练的实例
2020/01/18 Python
PyCharm配置anaconda环境的步骤详解
2020/07/31 Python
CSS3制作气泡对话框的实例教程
2016/05/10 HTML / CSS
如何用H5实现一个触屏版的轮播器的实例
2017/01/09 HTML / CSS
HSRP的含义以及如何工作
2014/09/10 面试题
信用社实习人员自我鉴定
2013/09/20 职场文书
理工大学毕业生自荐信范文
2014/02/22 职场文书
竞聘演讲稿精彩开头和结尾
2014/05/14 职场文书
环境整治工作方案
2014/05/18 职场文书
社区先进事迹材料
2014/05/19 职场文书
检查机关党的群众路线个人整改措施
2014/10/04 职场文书
2014年话务员工作总结
2014/11/19 职场文书
酒店服务员岗位职责
2015/02/09 职场文书
关爱留守儿童捐款倡议书
2015/04/27 职场文书
MySQL pt-slave-restart工具的使用简介
2021/04/07 MySQL
详解缓存穿透击穿雪崩解决方案
2021/05/28 Redis